Node.jsでの作業の流れの視覚化

Node.jsでの非同期プログラミング、コールバック、process.NextTick()の使用に関する前のトピックへのコメントでは、ノンブロッキングコードを使用してどの程度のパフォーマンスが得られるか、または得られるかについて多くの質問がありました。 私はこれを明確に実証しようとします:)この記事は主にNode.js(およびその構成のlibeio)の作業のいくつかの側面を明確にすることを目的としています。



読み取りをブロックするサーバーによるリクエスト処理の例:





まず、ノンブロッキングI / Oを使用することの有用性についてコメントします。 原則として、Node.jsでブロック操作を使用するのはアプリケーションの初期化段階のみであり、常にそうとは限りません。 どのような場合でも正しいエラー処理にはtry / catchの使用が必要になるため、非ブロッキング操作を使用する場合のコードは、ブロッキング操作を使用する場合よりも複雑になりません。

覚えておく必要があるのは、非ブロック操作に対する要求がlibeioスレッドよりも多い場合です。 この場合、新しいリクエストは実行をキューに入れてブロックしますが、これはプログラマーにとって透過的に行われます。



ノンブロッキング読み取りのサーバーによるリクエストの処理の例:





もちろん、これら2つの例は、サーバーのパフォーマンスが最大化される場合を示しています。 ただし、非ブロッキング読み取りの利点は、着信リクエスト間でいつでもあり、最悪の場合でも、リクエスト処理プロセスにlibeioストリームを含めることで生産的に改善できます。

リクエストを処理するための合計時間(最初のリクエストを送信してから最後の処理結果を受信するまでの時間、右側の青い数字)は、すべてのリクエストに十分なスレッドがある場合、いずれの場合も短くなります。 ただし、最悪の場合でも、同期読み取りを使用する場合、この時間は処理時間を超えません。



2つの要求がほぼ同時に到着した場合の処理​​時間を短縮する例:





そして、ここでNode.jsプログラマーによって使用され、ほとんどの開発者を当惑させる可能性のある最も非論理的なトリックに行き当たります。 I / Oがリクエストの処理時間の大部分を占める場合、残りのコードは最適化されません。 ただし、me​​mcachedからデータを取得するのにかかる時間は、アプリケーションのビジネスロジックとテンプレートの実行時間に比例する可能性があります。 また、Node.jsプロセスのメモリでキャッシュまたはデータベース( DirtyまたはAlfred )を使用する場合、データベースでの作業時間はアプリケーションの他の部分の作業時間よりも短くなる可能性があります。 したがって、コードを個別の部分に分割してコールバックを呼び出すには、process.nextTick()を使用します。



// blocking callbacks function func1_cb(str, cb) { var res = func1(str); cb(res); } function func2_cb(str, cb) { var res = func2(str); cb(res); } // non-blocking callbacks function func1_cb(str, cb) { var res = func1(str); process.nextTick(function () { cb(res); }); } function func2_cb(str, cb) { var res = func2(str); process.nextTick(function () { cb(res); }); } // usage example func1_cb(content, function (str) { func2_cb(str, function (result) { // work with result }); });
      
      





calc(1)とcalc(2)の実行を分離するこのアプローチを使用すると、リクエストがほぼ同時に到着する前の例の合計処理時間は変わりませんが、最初のリクエストは後でクライアントに返されます。



2つのリクエストがほぼ同時に到着した場合のprocess.nextTick()からの「害」の例:





ただし、これはprocess.nextTick()の適用性に関して最悪のケースです。 最初に検討した例のように、リクエストがめったに来ない場合、process.nextTick()による害はまったくありません。 要求が「平均」頻度で到着する場合、process.nextTick()を使用すると、実行スレッドの中断時に、新しい要求の初期処理と非ブロッキング読み取りの開始が発生する可能性があるため、要求の処理が高速化されます。 この場合、1つのリクエストの合計処理時間と平均処理時間が短縮されます。



process.nextTick()の「良い」例:





小さなトピックを要約します。 まず、Node.jsを使用する場合は、ノンブロッキング入出力を使用する必要があります。 標準数のlibeioストリームではなく、より少ない数のlibeioストリームが使用される場合、または多数の着信要求がある場合でも望ましいです。 新たな問題は、キャッシングとインプロセスDBの助けを借りて取り除くことができ、他の並列化テクノロジーの使用と大きな違いはありません。 第二に、process.nextTick()を「平均して」使用すると、サーバーのパフォーマンスが向上する可能性があり、一般的には有害というよりも有用です。



UPD(02.02):わずかに改善された回路。 ソースはgithub.com/Sannis/papers_and_talks/tree/master/2011_node_article_async_process_nexttickで入手できます。



All Articles