高性能ネットワーキングコードを何年も書いている人として(私の博士論文は、 マルチコアシステムに適合した分散アプリケーション用のキャッシュサーバー )、ネットワークサーバーモデルの基本についての説明を完全に見逃したり省略したりする、このテーマに関する多くのチュートリアルを目にします。したがって、この記事は、ネットワークサーバーモデルの概要と比較に役立つことを目的としており、高性能ネットワークコードの記述から謎を取り除くことを目的としています。
この記事は、「システムプログラマー」を対象としています。 バックエンド開発者 ネットワークサーバーコードを実装して、アプリケーションの低レベルの詳細を処理する人。これは通常、 C ++ または C ただし、最近のほとんどの言語とフレームワークは、さまざまなレベルの効率で、まともな低レベルの機能を提供します。
コアを追加することでCPUのスケーリングが容易になるため、これらのコアを可能な限り使用するようにソフトウェアを適応させるのは当然であるということを常識としています。したがって、問題は、複数のCPUで並行して実行できるスレッド(またはプロセス)間でソフトウェアをどのように分割するかということです。
また、読者は、「同時実行性」は基本的に「マルチタスク」、つまり同時にアクティブなコードの複数のインスタンス(同じコードであろうと異なるものであろうと関係ありません)を意味することを知っていると思います。並行性は単一のCPUで実現でき、現代以前は通常はそうでした。具体的には、同時実行性は、単一のCPU上の複数のプロセスまたはスレッドをすばやく切り替えることで実現できます。これは、実際にはそうではなかったものの、ユーザーがアプリケーションが同時に実行されていると認識できるように、古いシングルCPUシステムが同時に多くのアプリケーションを実行することに成功した方法です。一方、並列処理とは、具体的には、コードが文字通り、複数のCPUまたはCPUコアによって同時に実行されていることを意味します。
初心者向けの機械学習チュートリアル
この説明の目的上、スレッドまたは完全なプロセスについて話している場合は、ほとんど関係ありません。最新のオペレーティングシステム(Windowsを除く)は、プロセスをスレッドとほぼ同じくらい軽量に扱います(または、場合によっては、スレッドはプロセスと同じくらい重い機能を備えています)。現在、プロセスとスレッドの主な違いは、プロセス間またはスレッド間の通信とデータ共有の機能にあります。プロセスとスレッドの区別が重要な場合は、適切なメモを作成します。それ以外の場合は、このセクションの「スレッド」と「プロセス」という言葉は同じ意味であると考えてください。
この記事では、特に次の3つのタスクを実装するネットワークサーバーコードについて説明します。
これらのタスクをプロセス間で分割するための一般的なネットワークサーバーモデルがいくつかあります。 すなわち:
これらは、学術コミュニティで使用されているネットワークサーバーモデル名であり、少なくとも一部の「野生の」同義語を見つけたことを覚えています。 (もちろん、名前自体はそれほど重要ではありません。本当の価値は、コードで何が起こっているかを推論する方法にあります。)
これらの各ネットワークサーバーモデルについては、次のセクションで詳しく説明します。
MPネットワークサーバーモデルは、特にマルチスレッドについて学ぶときに、誰もが最初に学んだモデルです。 MPモデルには、接続を受け入れる「マスター」プロセスがあります(タスク#1)。接続が確立されると、マスタープロセスは新しいプロセスを作成し、接続ソケットをそれに渡します。したがって、接続ごとに1つのプロセスがあります。この新しいプロセスは通常、単純で順次的なロックステップの方法で接続を処理します。接続から何かを読み取り(タスク#2)、計算を実行し(タスク#3)、書き込みを行います(タスク#2)。再び)。
MPモデルは 非常に 実装は簡単で、プロセスの総数がかなり少ない限り、実際には非常にうまく機能します。どれくらい低いですか?答えは、タスク#2と#3が何を伴うかによって異なります。経験則として、プロセスまたはスレッドの数がCPUコアの数の約2倍を超えてはならないとしましょう。同時にアクティブなプロセスが多すぎると、オペレーティングシステムはスラッシングに多くの時間を費やす傾向があり(つまり、使用可能なCPUコアでプロセスまたはスレッドをジャグリングする)、そのようなアプリケーションは通常、CPUのほぼすべてを消費することになります。 「sys」(またはカーネル)コードでの時間。実際に役立つ作業はほとんど行いません。
長所: 実装は非常に簡単で、接続数が少ない限り非常にうまく機能します。
短所: プロセスの数が多くなりすぎると、オペレーティングシステムに過負荷がかかる傾向があり、ペイロード(計算)フェーズが終了するまでネットワークIOが待機するため、遅延ジッターが発生する可能性があります。
SPEDネットワークサーバーモデルは、Nginxなどの比較的最近の有名なネットワークサーバーアプリケーションによって有名になりました。基本的に、3つのタスクすべてを同じプロセスで実行し、それらの間で多重化します。効率的にするには、次のようなかなり高度なカーネル機能が必要です。 epoll そして kqueue 。このモデルでは、コードは着信接続とデータの「イベント」によって駆動され、次のような「イベントループ」を実装します。
これらはすべて単一のプロセスで実行され、通常はMPモデルのパフォーマンスを低下させるプロセス間のコンテキスト切り替えを完全に回避するため、非常に効率的に実行できます。ここでの唯一のコンテキストスイッチはシステムコールからのものであり、それらは、いくつかのイベントが関連付けられている特定の接続にのみ作用することによって最小限に抑えられます。このモデルは、ペイロード作業(タスク#3)が過度に複雑でなく、リソースを大量に消費しない限り、数万の接続を同時に処理できます。
ただし、このアプローチには2つの大きな欠点があります。
これらの理由から、より高度なモデルが必要になります。
長所: オペレーティングシステムでパフォーマンスが高く、簡単である可能性があります(つまり、OSの介入が最小限で済みます)。必要なCPUコアは1つだけです。
短所: (使用可能な数に関係なく)単一のCPUのみを使用します。ペイロードの動作が均一でない場合、応答の遅延が不均一になります。
SEDAネットワークサーバーモデルは少し複雑です。複雑なイベント駆動型アプリケーションを、キューで接続された一連のステージに分解します。ただし、注意深く実装しないと、MPの場合と同じ問題が発生する可能性があります。それはこのように動作します:
理論的には、このモデルは任意に複雑になる可能性があり、ノードグラフにはループ、他の同様のアプリケーションへの接続、またはノードが実際にリモートシステムで実行されている場所が含まれる可能性があります。ただし、実際には、明確に定義されたメッセージと効率的なキューがあっても、システム全体の動作について考えたり、推論したりするのは扱いにくい場合があります。各ノードで実行される作業が短い場合、メッセージパッシングオーバーヘッドは、SPEDモデルと比較してこのモデルのパフォーマンスを破壊する可能性があります。このモデルの効率はSPEDモデルの効率よりも大幅に低いため、通常、ペイロード作業が複雑で時間がかかる状況で使用されます。
長所: 究極のソフトウェアアーキテクトの夢:すべてがきちんとした独立したモジュールに分離されています。
短所: モジュールの数だけで複雑さが増す可能性があり、メッセージキューイングは直接メモリ共有よりもはるかに低速です。
AMPEDネットワークサーバーは、SEDAの使い慣れた、モデル化が容易なバージョンです。異なるモジュールやプロセスはそれほど多くなく、メッセージキューもそれほど多くありません。仕組みは次のとおりです。
ここで重要なことは、ペイロード作業が、接続の数に関係なく、固定された(通常は構成可能な)数のプロセスで実行されることです。ここでの利点は、ペイロードが任意に複雑になる可能性があり、ネットワークIOに影響を与えないことです(これは遅延に適しています)。ネットワークIOを実行しているプロセスは1つだけなので、セキュリティが向上する可能性もあります。
長所: ネットワークIOとペイロード作業の非常にクリーンな分離。
短所: プロセス間でデータをやり取りするためにメッセージキューを利用します。これは、プロトコルの性質によっては、ボトルネックになる可能性があります。
SYMPEDネットワークサーバーモデルは、多くの点でネットワークサーバーモデルの「聖杯」です。これは、独立したSPED「ワーカー」プロセスの複数のインスタンスがあるようなものだからです。これは、単一のプロセスがループ内の接続を受け入れ、それらをワーカープロセスに渡すことによって実装されます。各プロセスには、SPEDのようなイベントループがあります。これはいくつかの非常に好ましい結果をもたらします:
実際、これはNginxの新しいバージョンが行うことです。それらは少数のワーカープロセスを生成し、それぞれがイベントループを実行します。さらに改善するために、ほとんどのオペレーティングシステムは、複数のプロセスがTCPポートで着信接続を個別にリッスンできる機能を提供し、ネットワーク接続の操作専用の特定のプロセスを不要にします。作業中のアプリケーションをこのように実装できる場合は、そうすることをお勧めします。
長所: 制御可能な数のSPEDのようなループを備えた、厳密なCPU使用率の上限。
短所: 各プロセスにはSPEDのようなループがあるため、ペイロードの作業が不均一な場合、通常のSPEDモデルと同様に、レイテンシーが再び変動する可能性があります。
アプリケーションに最適なアーキテクチャモデルを選択することに加えて、ネットワークコードのパフォーマンスをさらに向上させるために使用できるいくつかの低レベルのトリックがあります。より効果的なもののいくつかの簡単なリストは次のとおりです。
将来のブログ投稿で、これらのほか、採用する追加のテクニックやトリックについて詳しく説明する可能性があります。しかし今のところ、これは、高性能ネットワークコードを作成するためのアーキテクチャの選択、およびそれらの相対的な長所と短所に関して、有用で有益な基盤を提供することを願っています。