私が解決しようとしていること:特定のポートでリッスンするErlang TCPサーバーがあり(コードはある種の外部向けインターフェイス/ APIに存在する必要があります)、各着信接続はによって処理される必要がありますgen_server
(つまり、gen_tcp:accept
コード化する必要があります) )の内部gen_server
ですが、実際には、着信接続を受け入れる事前定義された数のプロセスを最初に生成したくありません)。それはどういうわけか可能ですか?
4 に答える
基本的な手順
gen_server
次の手順を実行する1 つの静的プロセス (またはカスタム プロセスとして実装) が必要です。
- を使用して着信接続をリッスンします
gen_tcp:accept/1
- 接続を返すたびに、スーパバイザにワーカー プロセス (別の
gen_server
プロセスなど)を生成するように指示します。 - このプロセスの pid を取得します
gen_tcp:controlling_process/2
新しく返されたソケットとその pid で呼び出します- そのプロセスにソケットを送信します
注:この順序で実行する必要があります。そうしないと、所有権が引き渡される前に、新しいプロセスがソケットを使用する可能性があります。これを行わないと、新しいプロセスがすでに引き継がれているときに古いプロセスがソケットに関連するメッセージを取得し、パケットがドロップまたは誤って処理される可能性があります。
リッスン プロセスの責任は 1 つだけである必要があります。それは、新しい接続のためにワーカーを生成することです。このプロセスは を呼び出すとブロックされますがgen_tcp:accept/1
、開始されたワーカーは進行中の接続を同時に処理するため、これは問題ありません。受け入れ時にブロックすることで、新しい接続が開始されたときの応答時間が最短になります。プロセスがその間に他のことを行う必要がある場合は、gen_tcp:accept/2
タイムアウトの間にインターリーブされた他のアクションと一緒に使用できます。
スケーリング
複数のプロセスを
gen_tcp:accept/1
1 つのリッスン ソケットで待機させることができるため、同時実行性がさらに向上し、受け入れの待ち時間が最小限に抑えられます。別の最適化は、新しいソケットを受け入れた後の待ち時間をさらに最小限に抑えるために、一部のソケット ワーカーを事前に開始することです。
proc_lib
3 つ目と最後の方法は、 (詳細)を使用して独自のカスタム プロセスに OTP 設計原則を実装することで、プロセスをより軽量にすることです。gen_server
ただし、これは、ベンチマークを行って、動作が遅くなるという結論に達した場合にのみ行う必要があります。
の問題gen_tcp:accept
はブロックすることです。そのため、 内で呼び出すとgen_server
、サーバーが他のメッセージを受信するのをブロックします。タイムアウトを渡すことでこれを回避しようとすることができますが、最終的には回避するのが最善のポーリングの形式になります。代わりに、Kevin Smith の gen_nb_serverを試すことができます。ドキュメント化されていない内部関数prim_inet:async_accept
やその他のprim_inet
関数を使用して、ブロックを回避します。
http://github.com/oscarh/gen_tcpdをチェックアウトし、handle_connection関数を使用して、取得したプロセスをgen_serverに変換することをお勧めします。
スティーブが言ったように、「prim_inet:async_accept(Listen_socket, -1)」を使用する必要があります。これで、非同期の受け入れ呼び出しを使用したため、着信接続は handle_info コールバック (インターフェイスも gen_server であると仮定) によって受け入れられます。
接続を受け入れると、別の ger_server (私は gen_fsm をお勧めします) を生成し、「gen_tcp:controlling_process(CliSocket, spwned プロセスの Pid)」を呼び出して「制御プロセス」として作成できます。
この後、ソケットからのすべてのデータは、インターフェイス コードではなく、そのプロセスによって受信されます。そのように、別の接続のために新しい制御プロセスが生成されます。