Rack の並行性 - rack.multithread、async.callback、またはその両方? 質問する

Rack の並行性 - rack.multithread、async.callback、またはその両方? 質問する

私は、Rack での同時リクエスト処理のオプションを完全に理解しようとしています。async_sinatra を使用してロングポーリング アプリを構築し、現在はthrow :asyncThin の --threaded フラグを使用してベアメタル Rack を試しています。このテーマについては理解していますが、理解できない点がいくつかあります。(同時実行を並列処理と勘違いしているわけではありませんし、GIL によって課せられる制限も理解しています)。

Q1. 私のテストでは、thin --threaded(つまりrack.multithread=true) はリクエストを別々のスレッドで同時に実行します (EM を使用していると想定)。つまり、長時間実行されるリクエスト A はリクエスト B をブロックしません (IO は別)。つまり、私のアプリケーションでは、同時実行を実現するために特別なコーディング (コールバックなど) は必要ありません (ここでも、ブロッキング DB 呼び出し、IO などは無視します)。これは私が観察したと信じていることですが、正しいでしょうか?

Q2. 並行性を実現するもう1つの、よりよく議論される手段は、とですEventMachine.deferthrow :async厳密に言えば、リクエストはないスレッドを使用して処理されます。これらはシリアルに処理されますが、負荷の高い処理とコールバックは EventMachine に渡され、EventMachine は async.callback を使用して後で応答を送信します。リクエスト A が EM.defer に作業をオフロードした後、リクエスト B が開始されます。これは正しいです?

Q3. 上記の内容がほぼ正しいと仮定すると、一方の方法が他方の方法よりも特に優れている点はありますか?明らかに--threaded魔法の弾丸のように見えます。欠点はあるのでしょうか? ないのなら、なぜみんながasync_sinatra/ throw :async/について話しているasync.callbackのでしょうか? おそらく前者は「高負荷時に Rails アプリをもう少し速くしたい」ということであり、後者は長時間実行されるリクエストが多いアプリに適しているのでしょうか? あるいは、スケールが要因なのでしょうか? これは単なる推測です。

--no-epoll私はMRI Ruby 1.9.2でThin 1.2.11を実行しています。(参考までに、フラグを使用する必要があります。長年の、解決したはずの、実際には解決していない問題EventMachine の epoll と Ruby 1.9.2 の使用について。これは本題から外れますが、ご意見は大歓迎です。

ベストアンサー1

注: 私は、非同期 Rack 拡張機能を実装するすべての Web サーバー (つまり Rainbows!、Ebb、Puma の将来のバージョンなど) の同義語として Thin を使用します。

質問1.正解です。レスポンス生成 (別名call) を でラップしEventMachine.defer { ... }、EventMachine がそれを組み込みのスレッド プールにプッシュします。

質問2.async.callbackをと組み合わせて使用​​するのEM.deferは、実際にはあまり意味がありません。基本的にスレッド プールも使用することになり、Q1 で説明したのと同様の構造になってしまうからです。 を使用するのは、 IO に eventmachine ライブラリのみを使用する場合に意味があります。が通常の Rack 応答を引数として呼び出されるasync.callbackと、Thin は応答をクライアントに送信します。env['async.callback']

本文が の場合EM::Deferrable、Thin はその deferrable が成功するまで接続を閉じません。秘密ですが、ロング ポーリング以上の処理が必要な場合 (つまり、部分的な応答を送信した後も接続を開いたままにする場合) は、やステータス コード をEM::Deferrable使用せずに、 を本文オブジェクトとして直接返すこともできます。throw :async-1

Q3.正解です。スレッドサービングかもしれない変更されていない Rack アプリケーションの負荷を改善します。Ruby 1.9.3 を搭載した私のマシンでは、シンプルな Sinatra アプリケーションで 20% の改善が見られました。すべてのコアを利用できる Rubinius または JRuby で実行すると、さらに改善されます。2 番目のアプローチは、イベント方式でアプリケーションを作成する場合に役立ちます。

Rack の上に多くの魔法やハックを投入して、イベントのないアプリケーションでそれらのメカニズムを利用できるようにすることが可能です (em-synchrony または sinatra-synchrony を参照)。しかし、そうするとデバッグと依存関係の地獄に陥ることになります。

非同期アプローチは、イベントベースのアプローチで解決するのが最も適しているアプリケーションでは、本当に意味があります。ウェブチャットただし、ロングポーリングを実装するためにスレッド化アプローチを使用することはお勧めしません。ポーリング接続ごとにスレッドがブロックされるためです。これにより、大量のスレッドまたは処理できない接続が残ってしまいます。EM のスレッド プールのサイズはデフォルトで 20 スレッドであり、プロセスごとに待機中の接続が 20 に制限されます。

着信接続ごとに新しいスレッドを作成するサーバーを使用することもできますが、スレッドの作成はコストがかかります(MacRubyを除く。ただし、私はMacRubyを本番アプリケーションで使用することはありません)。例は次のとおりです。サービスそしてネットhttpサーバー理想的には、リクエストとスレッドの n:m マッピングが必要です。しかし、それを提供するサーバーは存在しません。

このトピックについてもっと知りたい方は、Rocky Mountain Ruby(および他の多くのカンファレンス)でこのことについてプレゼンテーションしました。ビデオ録画は以下にあります。コンフリークスで

おすすめ記事