EPOLL

Supported by

 

TsgcWebSocketServer

TsgcWebSocketHTTPServer

 

*sgcWebSockets Enterprise Editionが必要です。

 

Linux の EPOLL は、Indy がデフォルトで行うように接続ごとに 1 つのスレッドを使用する代わりに、限られたスレッドプールを使用して何千もの接続を処理できる API です。

Indy サーバーで EPOLL を有効にするには、IOHandlerOptions プロパティに移動して IOHandler タイプとして iohIEPOLL を選択してください。


Server.IOHandlerOptions.IOHandlerType := iohEPOLL;
Server.IOHandlerOptions.EPOLL.EPOLLThreads := 0;
Server.IOHandlerOptions.EPOLL.WorkOpThreads := 0;
Server.IOHandlerOptions.EPOLL.PartialRequestTimeout := 0;
Server.IOHandlerOptions.EPOLL.TCPKeepAlive.Enabled := True;
Server.IOHandlerOptions.KeepAliveTimeout := 120;

 

EPOLLThreads は EPOLL 非同期リクエスト(オーバーラップ操作)に使用されるスレッドです。デフォルト値はゼロで、プロセッサ数に基づいてスレッド数が計算されます(cpucount 関数がサポートされていない Delphi 7 および 2007 では 32 に設定されます)。スレッド数は手動で調整できます。

WorkOpThreads は、リクエストを処理するために待機させておくワーカースレッドの最小数を設定します(デフォルトは 0)。EPOLL スレッドはソケットが準備できたことを検出して、その処理をこのプールに引き渡すだけなので、応答の遅いクライアントによってブロックされることはありません。このプールは、すべてのワーカーがすでに処理中の場合、あらかじめ決められた上限まで必要に応じて拡張されるため、事前にサイズを見積もっておく必要はありません。この値を大きくするのは、最初のリクエストが届く前にプールを準備しておきたい場合だけにしてください。そうすることで、接続が一度に集中する起動直後にスレッド生成のコストを支払わずに済みます。

2026.9.0 での変更点。 以前の WorkOpThreads は、すべての接続をこれらのスレッドのいずれか 1 つに固定していたため、同じ接続に対するすべてのリクエストは同じスレッドで実行され、WorkOpRoundRobin は新しい接続をそれらのスレッドにどのように振り分けるかを決めていました。この固定は廃止されました。リクエストの残りを待って停止している接続が、同じスレッドに割り当てられた他のすべての接続をブロックし、サーバーを停滞させていたためです。同じ接続に対するリクエストが異なるスレッドで実行されるようになったため、データベース接続やスレッドローカルキャッシュなど、スレッドごとに保持しているものが接続に追従するとみなしてはなりません。WorkOpRoundRobin は存在しなくなりました。コードから削除してください。また、Object Inspector で有効にしていた場合は、フォームの .dfm からも WorkOpRoundRobin の行を削除してください。そうしないとフォームが読み込まれません。

PartialRequestTimeout は、長さがすでにわかっている HTTP リクエストボディの残りをサーバーが待機する時間をミリ秒単位で指定します(デフォルトは 0 で、これは無効を意味します)。これを設定しない場合、リクエストボディが単一のレディネスイベント内にすべて到達しない場合(例えば、複数のパケットに分けて送信された大きな POST など)、そのリクエストボディは黙って破棄され、応答なしに接続が閉じられます。この待機処理は、多数の接続を処理するためブロックしてはならない EPOLL スレッド上では決して実行されず、上記で説明したワーカースレッドのプール上で実行されます。このプールは、あらかじめ決められた上限まで必要に応じて拡張されます。そのため、応答の遅い、あるいは応答しないクライアントがこのプールを使い果たして他の接続を妨げることはありません。

TCPKeepAlive は、オペレーティングシステムがアイドル状態の接続を調査し、応答がなくなったまま消失したピアを検出できるようにします(デフォルトは無効)。TCPKeepAlive.Enabled を True に設定すると、OS は接続が TCPKeepAlive.Time 秒(デフォルトは 60)アイドル状態になった後に調査を開始し、ピアが応答するか接続が切断されるまで TCPKeepAlive.Interval 秒(デフォルトは 10)ごとにこれを繰り返します。

KeepAliveTimeout は、指定した秒数(デフォルトは 0 で、これは無効を意味します)を超えてアイドル状態が続いた接続を閉じます。影響を受けるのはアイドル状態の接続のみで、読み取り中のリクエストや書き込み中のレスポンスがこのタイムアウトによって中断されることはありません。これは、デフォルトの 1 スレッド = 1 接続の IOHandler がサイレントな接続を解放する動作に最も近いものであり、オペレーティングシステムやピア側の協力に依存しません。

 

数千の接続を処理する必要がある場合は、Linux サーバーで EPOLL を有効にすることをお勧めします。サーバーが最大 100 の同時接続しか処理しない場合は、デフォルトの Indy スレッドモデルのままで問題ありません。

 

 

OnDisconnect イベントが発生しない

 

EPOLL はデフォルトの Indy IOHandler とは異なる動作をします。デフォルトの Indy IOHandler では、すべての接続がスレッドで実行され、これらのスレッドは常に実行されて接続がアクティブかどうかを確認します。そのため、切断があった場合、短時間で通知されます。

 

EPOLL の動作は異なります。Indy がデフォルトで行う 1 スレッド = 1 接続ではなく、すべての接続を処理するスレッドプールがあります。EPOLL では、接続がまだ生きているかどうかを検出する唯一の方法はソケットへの書き込みを試みることです。エラーがある場合は接続が閉じられていることを意味します。切断を検出するには 3 つのオプションがあります:

 

1. TsgcWebSocketClient を使用する場合は、Options プロパティで有効にできます。CleanDisconnect := True(デフォルトは無効)。有効にすると、クライアントは切断前に切断をサーバーに通知するメッセージを送信するため、サーバーはこのメッセージを受信し、OnDisconnect イベントが発生します。

2. サーバー側でハートビートを有効にできます。例えば60秒ごとに有効にすると、接続されているすべてのクライアントにpingを送信しようとし、切断されているクライアントがある場合はOnDisconnectが呼び出されます。

3. サーバーの IOHandlerOptions で TCPKeepAlive を有効にしたり、KeepAliveTimeout を設定したりできます。これにより、例えばクライアントがクラッシュした場合やネットワーク接続がクローズメッセージを送信せずに切断された場合など、応答がなくなったピアをサーバー自身が検出して接続を閉じることができ、クライアント側が何かを送信することに依存しません。

 

Linux接続制限

 

同時にオープンできる接続数を増やしたい場合は、次のコマンドを使用してください。

 

ulimit -n 10000

 

前のコマンドは開いているファイルディスクリプターの最大数を 10000 に設定します。