HN 日本語サマリー

← 一覧へ戻る
プログラミング

Kino: Ruby 4.0 用の高性能 Ractor Web サーバー

Kino: A high-performance Ractor web server for Ruby 4.0 (github.com)

58 pointsby ksec11 コメント

要約

Kinoは、Ruby 4.0以降向けの高性能なRactorベースのWebサーバーです。従来のRubyのプロセスフォーク方式と比較して、メモリ使用量を大幅に削減し、I/O負荷の低いエンドポイントやCPUバウンドなタスクにおいて高いパフォーマンスを発揮します。Ractorモードと、RailsなどのRactor非対応アプリも実行できるスレッドフォールバックモードを提供し、Pumaに似た使いやすさを目指しています。

全文翻訳

Kino Ruby 4.0以降向けの高性能Ractor Webサーバー。 RubyのスレッドはRubyコードを並列実行できないため、本番環境ではコアごとにプロセスをフォークし、各コピーのメモリコストを負担します。Kinoは、1つの小さなプロセス内で、すべてのコアでコードを実行します。Rust(tokio + hyper)のフロントエンドがネットワークを所有し、並列RactorがRack 3アプリを実行し、スレッドフォールバックモードがそれ以外(Railsを含む)を実行します。 高速。 実際の8コアサーバーでは、Kinoの各モードは、I/O負荷の低いエンドポイントでPumaのフォーククラスターよりも1.5〜2倍高速です。Ractorモードは純粋なCPUでも30%以上優れています。ベンチマークは以下を参照してください。 メモリ使用量が少ない。 単純なベンチマークRactorアプリでは約7分の1、フォールバックスレッドモードでRailsを提供しているPumaクラスターよりも約4分の1のメモリで済みます。 フォークせずに並列実行。 Ractorモードは、同じ小さなプロセス内で、Kino自身のGVLバウンドスレッドモードよりもCPUワークを5倍以上高速に実行します。 本番環境の配管が含まれています。 グレースフルドレイン、クラッシュ監視と再起動、503バックプレッシャー付きのバウンドキュー、リクエストタイムアウト、強化された受付(slowlorisおよびTLSハンドシェイクデッドライン、接続およびボディサイズキャップ)、エラー追跡用のon_errorフック、TLS(rustls)、ライブ統計、非同期アクセスとアプリロギング。 理由を教えてくれます。 kino --check は、アプリがRactorモードをブロックしているものを正確にリストアップし、Ractor::IsolationErrorを自分でデコードする必要なく、発見します。 Puma形状。 同じワーカー×スレッドトポロジー、使い慣れた設定DSL、kino CLI。Pumaを実行できるなら、Kinoも実行できます。 注:RactorはRuby 4.0で公式に実験的であり、このサーバーも同様です。スレッドモードは堅牢です。それでも、Kinoは今日Ractorを実験するための最良の方法であり、Ractorが安定したときの最良のRactorサーバーとなることを目指しています。 目次 なぜ ベンチマーク インストール 使用法 設定ファイルとCLI kino --check リクエストタイムアウト 統計 ロギング タイマー待機 Rack 3準拠 Rails なぜ GVL(Global VM Lock)により、一度に実行できるRubyスレッドは1つだけです。すべてのコアを使用するために、Rubyサーバーはプロセスをフォークし、各フォークはアプリの完全なコピーのコストを負担します。Ractorにはこの制限がありません。各Ractorは独自のロックを持つため、1つのプロセスでRubyを並列実行できます。不足していたのは、それらにリクエストをディスパッチするサーバーでした。Ruby 4.0はRactor(Ractor::Port、shareable_proc、ロック競合の低減)を改良し、これを構築する価値があるものにしました。 Ractorサーバーがこのように構築される必要がある理由、そしてどのRust部分がここでRactorを高速にしているかについては、doc/why-kino.mdを参照してください。完全な設計ノートはdoc/architecture.mdにあります。 ベンチマーク 実際のサーバーで測定されました:AWS c7a.2xlarge(8コアAMD EPYC 9R14、16 GB、Amazon Linux 2023)。これは現実的なアプリサーバーサイズです。これらのテーブルは、サーバー自体を測定するため、意図的に小さな合成Rackアプリ(プレーンテキスト、10 KBボディ、CPUバウンドなfib、5 ms待機)を実行します。これはRactor共有可能であるため、Kinoはそれを:ractorモード(および比較のために:threaded)で実行します。実際のRailsアプリは異なります。Ractor共有可能ではないため、Kinoの:threadedフォールバックでのみ実行され、独自の数値が表示されます。Railsを参照してください。 Ruby 4.0.5(YJIT付き)、すべてのサーバーはデフォルト設定:Pumaは8ワーカー×3スレッドをフォークし、Kinoは1つのプロセス(8ワーカー;ractorモードでは各1スレッド、スレッドモードでは3スレッド)にとどまります。数値はwrkによるreq/s(8秒間隔、64接続、同じホスト)です。方法論:doc/benchmarks.md。 エンドポイント | Kino :ractor + lanes | :ractor, workers 32 | Kino :threaded | Puma (cluster) /plaintext | 229,534 | 250,222 | 182,997 | 216,994 /10k | 178,083 | 189,862 | 151,034 | 160,400 /cpu (fib) | 77,999¹ | 70,885 | 66,100 | 13,429 /io (5 ms) | 1,552 | 1,551 | 5,888 | 4,709 /io_native | 1,570 | 1,571 | 6,274 | 4,695 メモリは、持続的な負荷後のPSS(比例セットサイズ;注参照)によって、アプリに応じて2つの異なる物語を語ります。小さなベンチマークアプリ(Ractor共有可能なのでKinoは:ractorまたは:threadedで実行)。Kinoは:ractorモードで約7倍、:threadedモードでPumaクラスターの約10倍軽量です。この差は大きく、なぜなら取るに足らないアプリはほとんどすべてがワーカーごとのプライベートヒープであり、コピーオンライトでは共有できないからです。 アプリ | Kino (1プロセス) | Pumaクラスター (8ワーカー) | 比率 小さなアプリ | Kino :ractor (8×1) 148 MB | 1,068 MB | ~7× | Kino :threaded (8×3) 107 MB³ | 1,068 MB | ~10× 実際のRailsアプリ(Ractor共有不可能 — Kinoの:threadedフォールバックのみ、以下)。差は約4倍と小さいです。なぜなら、Railsの大きなフレームワークはPumaのフォーク間でコピーオンライトで共有されるからです。 アプリ | Kino :threaded (1プロセス) | Pumaクラスター (8ワーカー) | 比率 Rails hello-world | PSS 92 MB | 389 MB | ~4× 「+ lanes」は実験的なワーカーごとのキューディスパッチャー(lanes true)です。これは、ここで示されたどの構成よりも高速なプレーンテキスト/10kを投稿します。詳細:doc/benchmarks.md。 ¹ 標準設定、チューニングなし。Ractorモードはフォーククラスターよりも純粋なCPUで+34%(lanes使用で+22%)上回ります。スレッドモードは、すべての単一プロセスRubyサーバーがヒットするGVLの天井を示しています。古いCPUチューニングレシピは廃止されました。そのスレッド1の半分がデフォルトであり、tokio_threads 1の半分は実際のハードウェアで-12%のコストがかかります。doc/benchmarks.mdを参照してください。 ² 待機バウンドスループットはスロット ÷ 待機であり、デフォルトのカラムはクラスターの24スレッドに対して8つのシングルスレッドワーカーをもたらします。Kinoのスロットはプロセスではなくスレッドです。アプリが多くを待つ場合は、ワーカーを増やしてください。workers 32カラムはそのチューニングです。I/Oではクラスターより+25%(Kino.sleep経由で+34%)、それでも純粋なCPUではクラスターを上回っています。すべて1つの小さなプロセスで。コストはCPU負荷の低い行(8コアをオーバーサブスクライブする32 Ractor)です。アプリの待機プロファイルが必要とするトポロジーを選択してください。doc/benchmarks.mdを参照してください。 ³ MALLOC_ARENA_MAX=2(標準のRubyデプロイメント設定;Herokuのデフォルト)を使用。これがない場合、24スレッドが1つのglibcヒープを10 KBレスポンスで churning すると約670 MBになります。これはアリーナフラグメンテーションの足かせであり、リークではありません。Ractorモードはこれを回避します。doc/benchmarks.mdを参照してください。 一般的な最初のアイデアは、現在のサーバーを維持し、アプリをRactorプールでラップすることです。それも測定しました(同じボックス;分析はドキュメントにあります)。 エンドポイント | Kino :ractor (8×3) | Puma + ractor ラッパー | Falcon + ractor ラッパー /plaintext | 193,826 | 19,480 | 99,776 /cpu (fib) | 68,061 | 17,755 | 48,721 /io (5 ms) | 4,530 | 1,454 | 1,549 Rails Railsは現在Ractor共有可能ではないため、Kinoはそれを:threadedフォールバックで提供します。これはGVLバウンドの単一プロセスです。同じボックス上(examples/rails-hello、エッジRails、本番環境、8×5): アプリ | req/s | メモリ (PSS) Rails hello-world | Kino :threaded (1プロセス) 2,637 | 92 MB | Pumaクラスター (8ワーカー) 12,138 | 389 MB 正直なトレードオフ:Pumaのフォーククラスターは8コアすべてを使用するため、約4.6倍のスループットを提供します。ただし、メモリは約4倍かかります。RactorモードのRailsは、単一プロセスのメモリコストでスループットのギャップを埋めるでしょう。アップストリームのブロッカーはdoc/rails-on-ractors.mdで追跡されています。 要するに:小さな合成アプリでは、RactorモードはフォークレベルのCPU並列処理(Kino自身のGVLバウンドスレッドモードの5.8倍、クラスターより+34%)を1つのプロセスで、クラスターメモリの約1/7(PSSあたり)(実際のRailsアプリでは約4倍)で上回ります。すべてのKinoモードは、I/O負荷の低いエンドポイントでクラスターよりも1.5〜2.1倍高速です。macOSの数値(二次;すべてループバック天井に達します)とYJIT×Ractorの注意点はdoc/benchmarks.mdにあります。再現:メインテーブルの場合はbench/run.sh [秒] [同時接続数]、フォローアップ(CPUレシピ、トポロジー、スケーリング、ロギング、メモリ)の場合はbench/studies.sh。 インストール Ruby >= 4.0が必要です。Kinoをアプリケーションのバンドルに追加してください。 bundle add kino # または: gem install kino (バンドルの外で) またはGemfileに自分で追加してください: gem "kino" 次に、設定を生成して提供します。 bundle exec kino --init # kino.rb を書き込みます。すべてのディレクティブはインプレースで文書化されています bundle exec kino # config.ru + kino.rb を読み込み、:9292 で提供します (スタンドアロンのgemインストール後、kinoコマンドはbundle execなしで動作します。) Rustコンパイラは不要です:リリースバージョンは、Linux(x86_64/aarch64、glibcおよびmusl)およびmacOS(arm64)用のプリコンパイル済みネイティブgemを出荷します。他のプラットフォームでは、gemはインストール時にコンパイルされます。これにはRustツールチェーン、およびLinuxではclang/libclangが必要です。 使用法 require "kino" # RactorモードはRactor共有可能なアプリが必要です:何もキャプチャせず、設定をフリーズします。 app = Ractor.shareable_proc do |env| [200, { "content-type" => "text/plain" }, ["Hello from #{Ractor.current}"]] end Kino::Server.run(app, port: 92