一般的に、Node.js は 10,000 件の同時リクエストをどのように処理するのでしょうか? 質問する

一般的に、Node.js は 10,000 件の同時リクエストをどのように処理するのでしょうか? 質問する

Node.js は、シングルスレッドとイベント ループを使用して、リクエストを一度に 1 つだけ処理する (非ブロッキング) ことを理解しています。しかし、10,000 件の同時リクエストがあるとすると、どのように動作するのでしょうか。イベント ループはすべてのリクエストを処理するのでしょうか。時間がかかりすぎませんか。

マルチスレッド Web サーバーよりも高速になる理由が (まだ) わかりません。マルチスレッド Web サーバーはリソース (メモリ、CPU) の面で高価になることは理解していますが、それでも高速ではないでしょうか。おそらく私は間違っていると思います。このシングル スレッドが大量のリクエストで高速になる理由と、10,000 件などの大量のリクエストを処理するときに通常 (高レベルで) どのような処理が行われるかを説明してください。

また、その単一スレッドは、その大量のデータでも適切にスケーリングできるでしょうか? 私は Node.js を学び始めたばかりであることを念頭に置いてください。

ベストアンサー1

この質問をしなければならないということは、ほとんどの Web アプリケーションやサービスが何をするのかよくわかっていないということでしょう。おそらく、すべてのソフトウェアが次のことを行うと考えているのでしょう。

user do an action
       v
 application start processing action
   └──> loop ...
          └──> busy processing
 end loop
   └──> send result to user

しかし、これは Web アプリケーション、またはバックエンドとしてデータベースを使用するアプリケーションの動作方法ではありません。Web アプリケーションは次のように動作します。

user do an action
       v
 application start processing action
   └──> make database request
          └──> do nothing until request completes
 request complete
   └──> send result to user

このシナリオでは、ソフトウェアは実行時間のほとんどを、データベースが返されるのを待つために 0% の CPU 時間を使用して費やします。

マルチスレッド ネットワーク アプリ:

マルチスレッド ネットワーク アプリは、上記のワークロードを次のように処理します。

request ──> spawn thread
              └──> wait for database request
                     └──> answer request
request ──> spawn thread
              └──> wait for database request
                     └──> answer request
request ──> spawn thread
              └──> wait for database request
                     └──> answer request

そのため、スレッドはほとんどの時間を 0% の CPU 使用率で過ごし、データベースがデータを返すのを待ちます。その間、スレッドに必要なメモリを割り当てる必要があり、これにはスレッドごとに完全に独立したプログラム スタックなどが含まれます。また、スレッドを開始する必要があり、これは完全なプロセスを開始するほどコストは高くありませんが、それでも決して安価ではありません。

シングルスレッドイベントループ

私たちはほとんどの時間を 0% の CPU 使用率で過ごしているので、CPU を使用していないときにコードを実行してみませんか? そうすれば、各リクエストはマルチスレッド アプリケーションと同じ量の CPU 時間を取得しますが、スレッドを開始する必要がありません。そのため、次のようにします。

request ──> make database request
request ──> make database request
request ──> make database request
database request complete ──> send response
database request complete ──> send response
database request complete ──> send response

実際には、処理を支配するのはデータベースの応答時間であるため、どちらのアプローチもほぼ同じレイテンシでデータを返します。

ここでの主な利点は、新しいスレッドを生成する必要がないため、速度を低下させる大量の malloc を実行する必要がないことです。

魔法の、目に見えないスレッド

一見不思議なのは、上記の両方のアプローチがどのようにしてワークロードを「並列」で実行できるのかということです。その答えは、データベースがスレッド化されていることです。つまり、私たちのシングルスレッド アプリは、実際には別のプロセス、つまりデータベースのマルチスレッド動作を活用しているのです。

シングルスレッドアプローチが失敗する場所

シングルスレッド アプリは、データを返す前に CPU 計算を大量に実行する必要がある場合、大きな失敗をします。ここで私が言っているのは、データベースの結果を処理する for ループではありません。これは、依然としてほとんど O(n) です。私が言っているのは、フーリエ変換 (たとえば、mp3 エンコード)、レイ トレーシング (3D レンダリング) などのことです。

シングルスレッド アプリのもう 1 つの落とし穴は、1 つの CPU コアしか使用しないことです。したがって、クアッドコア サーバー (最近では珍しくありません) を使用している場合は、他の 3 つのコアは使用されません。

マルチスレッドアプローチが失敗する場所

スレッドごとに大量の RAM を割り当てる必要がある場合、マルチスレッド アプリは大きな失敗をします。まず、RAM の使用自体が、シングルスレッド アプリほど多くのリクエストを処理できないことを意味します。さらに悪いことに、malloc は低速で​​す。大量のオブジェクトを割り当てる (これは最近の Web フレームワークでは一般的です) と、シングルスレッド アプリよりも遅くなる可能性があります。これは通常、node.js が勝る点です。

マルチスレッドを悪化させるユースケースの 1 つは、スレッドで別のスクリプト言語を実行する必要がある場合です。通常、最初にその言語のランタイム全体を malloc する必要があり、次にスクリプトで使用される変数を malloc する必要があります。

したがって、C や Go、Java でネットワーク アプリケーションを作成する場合、スレッドのオーバーヘッドは通常それほど大きな問題にはなりません。PHP や Ruby を提供する C Web サーバーを作成する場合、JavaScript、Ruby、Python でより高速なサーバーを作成するのは非常に簡単です。

ハイブリッドアプローチ

一部の Web サーバーではハイブリッド アプローチが使用されています。たとえば、Nginx と Apache2 は、ネットワーク処理コードをイベント ループのスレッド プールとして実装しています。各スレッドはイベント ループを同時に実行し、シングル スレッドでリクエストを処理しますが、リクエストは複数のスレッド間で負荷分散されます。

シングルスレッドアーキテクチャの中には、ハイブリッドアプローチを採用しているものもあります。単一のプロセスから複数のスレッドを起動する代わりに、複数のアプリケーションを起動することができます。たとえば、クアッドコアマシンで4つのNode.jsサーバーを起動します。次に、ロードバランサーを使用してプロセス間でワークロードを分散します。集まるnode.js のモジュールはまさにこれを実行します。

実際には、これら 2 つのアプローチは技術的には互いに同一の鏡像です。

おすすめ記事