9

Nginx/Passenger に Rails 3 アプリがあり、Nginx/Thin (1.3.1) に移動したばかりです。ただし、私のアプリは Passenger よりも明らかに遅くなりました。多くのリクエストもタイムアウトします。

Thin は、イベント化された Web サーバーです。イベント Web サーバーについて読んだところによると、それらにはワーカーの概念がありません。1 人の「ワーカー」がすべてを処理します。したがって、1 つの要求が IO で待機している場合、シンは次の要求に進むだけです。イベント サーバーについて私が読んだ 1 つの説明では、イベント サーバーはシステム リソースによってのみバインドされるため、ワーカー ベースのサーバーと同等またはそれ以上のパフォーマンスが必要であると述べていました。

ただし、私の CPU 使用率はほとんどありません。私のメモリ使用量も非常に少なく、IO もあまり発生していません。私のアプリは、いくつかの MySQL クエリを作成するだけです。

ここでのボトルネックは何ですか?CPU が 100% になるまで、シン サーバーでリクエストを処理するべきではありませんか? イベントサーバーでアプリのパフォーマンスを向上させるために、アプリで何か違うことをする必要がありますか?

4

2 に答える 2

13

セルジオは正しいです。この時点で、アプリはおそらく従来の Apache/Passenger モデルよりも優れています。特に Ruby のようなシングル スレッド プラットフォームでイベント ルートを使用する場合、DB、キャッシュ サーバー、作成する可能性のある他の HTTP リクエストなど、何もブロックすることはできません。

これが、非同期 (イベント化) プログラミングを難しくしている理由です。通常、同期ディスク I/O または DNS 解決の形で、ブロックするのは簡単です。nodejs などの非ブロッキング (イベント化) フレームワークは、ブロックしているフレームワーク関数呼び出しを (ほとんど) 決して提供せず、すべてがコールバック (DB クエリを含む) を使用して処理されるという点で注意が必要です。

これは、シングルスレッドのノンブロッキング サーバーの心臓部を見ると、視覚化が容易になる可能性があります。

while( wait_on_sockets( /* list<socket> */ &$sockets, /* event */ &$what, $timeout ) ) {
    foreach( $socketsThatHaveActivity as $fd in $sockets ) {
        if( $what == READ ) {   // There is data availabe to read from this socket
            $data = readFromSocket($fd);
            processDataQuicklyWithoutBlocking( $data );
        }
        elseif ($what == WRITE && $data = dataToWrite($fd)) { // This socket is ready to be written to (if we have any data)
            writeToSocket( $fd, $data );    
        }
    }
}

上に表示されているものは、イベント ループと呼ばれます。wait_on_sockets通常、select、poll、epoll、kqueue などのシステム コールの形式で OS によって提供されます。processDataQuicklyWithoutBlocking に時間がかかりすぎると、OS によって維持されるアプリケーションのネットワーク バッファー (新しい要求、着信データなど) が最終的にいっぱいになり、$socketsThatHaveActivity が十分に速く処理されないため、新しい接続が拒否され、既存の接続がタイムアウトになります。 . これは、各接続が個別のスレッド/プロセスを使用して提供されるという点で、スレッド化されたサーバー (たとえば、典型的な Apache インストール) とは異なります。そのため、受信データは到着するとすぐにアプリに読み込まれ、送信データは遅延なく送信されます。 .

(たとえば)DBクエリを作成するときにnodejsのようなノンブロッキングフレームワークが行うことは、監視されているソケットのリスト($sockets)にDBサーバーのソケット接続を追加することです。 (のみ)その1つのソケットでスレッドがブロックされていません。代わりに、コールバックを提供します。

$db.query( "...sql...", function( $result ) { ..handle result ..} );

上記のように、db.query は、db サーバーでまったくブロックされることなく、すぐに返されます。これは、プログラミング言語自体が非同期関数 (新しい C# など) をサポートしていない限り、次のようなコードを頻繁に記述する必要があることも意味します。

$db.query( "...sql...", function( $result ) { $httpResponse.write( $result ); $connection.close(); } );

それぞれがイベント ループ (通常はノード クラスターを実行する方法) を実行しているプロセスが多数ある場合、またはスレッド プールを使用してイベント ループ (Java の jetty、netty など) を維持する場合は、決してブロックしないルールを多少緩和できます。 、C/C++ で独自に記述できます)。1 つのスレッドが何かでブロックされている間、他のスレッドは引き続きイベント ループを実行できます。しかし、十分な負荷がかかると、これらでも実行に失敗します。したがって、イベントが発生したサーバーでは絶対にブロックしないでください。

ご覧のとおり、イベント サーバーは通常、別の問題を解決しようとします。非常に多くの接続が開かれている可能性があります。彼らが得意とするところは、軽い計算でバイトをプッシュすることです (たとえば、comet サーバー、memcached などのキャッシュ、ワニス、nginx などのプロキシ、squid など)。スケーリングが向上しても、一般に応答時間が長くなる傾向があることは何の価値もありません (接続用にスレッド全体を予約することほど良いことはありません)。もちろん、同時接続数と同じ数のスレッドを実行することは、経済的/計算的に実行可能ではない場合があります。

問題に戻ります-Nginxは接続管理(イベントベース)に優れているため、Nginxを引き続き使用することをお勧めします-通常、HTTPキープアライブ、SSLなどを処理することを意味します。次に、これをRailsアプリに接続する必要がありますFastCGI を使用すると、ワーカーを実行する必要がありますが、完全にイベント化するためにアプリを書き直す必要はありません。また、Nginx が静的コンテンツを提供できるようにする必要があります。Rails ワーカーを、通常は Nginx の方が優れているものに縛り付けても意味がありません。このアプローチは一般に、特にトラフィックの多い Web サイトを運営している場合、Apache/Passenger よりもはるかに優れたスケーリングを実現します。

アプリ全体をイベント処理するように記述できれば素晴らしいのですが、Ruby でそれがどれほど簡単か難しいかはわかりません。

于 2012-05-09T02:28:55.707 に答える
3

はい、Thin はイベント I/O を実行しますが、HTTP 部分のみです。これは、リクエストの処理中に着信 HTTP データを受信できることを意味します。ただし、処理中に行うブロッキング I/O はすべてブロッキングのままです。MySQL の応答が遅い場合、シン リクエスト キューがいっぱいになります。

「より多くの」イベント Web サーバーについては、Rainbowsを確認してください。

于 2012-05-09T01:09:47.297 に答える