TCPバックログキャプチャは、Stratusモジュール上のサーバーアプリケーションがポートXでリスニングしている際に、ポートXへの接続を試みるクライアントとの接続確立を妨げる現象です。同じクライアントからの他のポートへの接続は正常に機能します。一般的な解決策はサーバーアプリケーションをシャットダウンして再起動することです。本記事ではバックログキャプチャが発生する原因と、より穏便な解決策について説明します。
TCPバックログは、保留中の接続のキュー、すなわち受信済みだがまだ受け入れられていない接続要求の列である。OpenVOS 17.1以前のリリースでコンパイルされたアプリケーション、およびOpenVOS 17.1でコンパイルされたがPOSIXライブラリを持たないアプリケーションでは、接続要求はアプリケーションによって受け入れられるまで確認応答が送信されない。クライアントは接続要求パケットを3~10回、間隔を徐々に延ばしながら再送信する。 正確な詳細はクライアントのオペレーティングシステムとその設定に依存します。ある時点でサーバーアプリケーションが接続要求を受け入れ、STCPスタックが確認応答を送信します。クライアントが再送を断念する前にこの確認応答が受信されれば、接続は確立され通信を開始できます。
バックログ捕捉シナリオにはいくつかの条件が必要である
- サーバーとクライアントの間で一時的なネットワーク障害が発生しています(ただし、クライアントとサーバーの間では発生していません)。これにより、クライアントの接続要求はサーバーに受信されますが、サーバーの接続確認応答はクライアントに受信されません。
または
サーバーアプリケーションが何らかの理由でaccept関数の呼び出しを遅延させている。
- クライアントは接続要求を継続的に送信します。つまり、現在の要求がタイムアウトすると、クライアントは新たな要求を送信します。クライアントのタイムアウト値は、サーバーのタイムアウト値よりも短く設定する必要があります。
- クライアントとサーバーの間に存在するステートフルファイアウォールまたはその他のデバイスが、クライアントの接続要求をタイムアウトさせ、サーバーへの応答を返さずにサーバーの接続確認を破棄する。このタイムアウト値は、サーバーのタイムアウト値よりも短く設定する必要がある。
残念ながら、この条件の組み合わせは一見したほどありえないことではない。
通常、ネットワーク障害が発生し、サーバーからのパケットがクライアントに到達できなくなるが、クライアントからのパケットはサーバーに到達できる状態から始まります。クライアントの接続要求パケットがサーバーに到達すると、バックログキューに格納されます。サーバーアプリケーションがacceptコードを呼び出すと、コードはバックログキューから接続要求を取得し、接続確認応答を送信します。 ネットワーク障害によりサーバーは自身のパケットに対する応答を受け取れないため、接続確認応答を再送信します。クライアントのTCPスタックも応答を得られていないため、接続要求を再送信します。 しばらくするとクライアントのTCPスタックは諦め、クライアントアプリケーションは新たな要求を送信します。サーバーは最初のリクエストへの応答を継続します。約1分半後(デフォルト)、サーバーはタイムアウトし、バックログキューから次の要求を取り出します。 一方クライアントは2回目のリクエストを再送信済みで、3回目を行っている可能性もある。これらのリクエストもバックログキューに存在する。他のクライアントが接続リクエストをバックログキューに配置できる可能性はあるが、成功させるにはクライアントのタイムアウト値が「サーバーのタイムアウト値 × バックログキュー内の接続リクエストの位置」よりも長くなければならない。 ネットワーク障害が復旧した後も、ステートフルファイアウォールは障害を永続化させます。サーバーのタイムアウトより短い期間しか状態を保持しないため、状態情報が失われた接続確認応答を黙って破棄し続けるからです。
これをどう認識しますか?
まずバックログが満杯か、少なくとも埋まりつつある状態です。このコマンドで接続のバックログキューを表示できます。
analyze_system -request_line (string match backlog -or syncnt (byte 3Bx) dump_st +
cbq -full -lport 7654 -faddr 0x) -quit
OpenVOS Release 17.0.2au, analyze_system Release 17.0.2au
現在のプロセスは 154、ptep 91CC7100、Noah_Davids.CAC
syncnt 6
backlog 5
ready 11:41:19
|
lport値はローカルポート番号であり、この場合は7654である。外部アドレス(faddr)値が0の場合、出力はLISTENINGソケットのみに制限される。バックログ値に1を加えた値がsyncnt値と等しい場合、バックログは満杯である。syncntが0より大きく増加している場合、バックログキューが埋まりつつある。バックログキューのサイズは、アプリケーションがlisten関数を呼び出す際に設定される。
次に、ネットワークプロトコルのトレースを確認する必要があります。トレースからは、最後の接続要求が応答されない一方で、以前の接続要求に対する接続確認応答が存在し、それらの応答が一切返答を得られていないことが示されます。
図1のトレースがこれを示している。各接続にはストリームインデックスが付けられ、色分けされている。クライアントからの最初の接続要求は時間0のフレーム1にある。確認応答はフレーム2で送信され、その後フレーム3で再送信される。フレーム4はクライアントが接続要求を再送信したものである。STCPは既に接続確認応答を送信しているため、フレーム4はフレーム5のTCP確認応答のみをトリガーする。 フレーム6と7は再送信された接続確認応答です。フレーム8はクライアントからの再送信された接続要求です。フレーム9は再送信された接続要求によってトリガーされたTCP確認応答であり、フレーム10は別の再送信された接続確認応答です。フレーム11はクライアントからの別の接続要求ですが、これは新しい2番目の接続用です。応答がないことに注意し、フレーム12は再送信された接続要求を示しています。 フレーム13は接続確認応答を示しているが、これは依然として最初の接続に対するものである。フレーム14は2番目の接続要求の再送信である。フレーム14は2番目の接続要求の別の再送信である。フレーム15はクライアントからの新たな3番目の接続要求を示し、フレーム16と17は再送信である。フレーム18は最初の接続に対する接続確認応答の別の再送信である。 フレーム19はさらに別の接続要求であり、フレーム20と21は再送信、フレーム22は4番目の新規接続要求である。最終的にフレーム23でSTCPは最初の接続要求を断念しリセットを送信、続くフレーム24で2番目の接続要求に対する接続確認を送信し、フレーム25で再送信が行われる。この時点で概ね理解いただけたと思う。
STCPはフレーム0からフレーム23までタイムアウトするのに、合計106.9秒を要した。 クライアントはタイムアウト時にリセットを送信しないが、2回目と3回目の接続要求開始間の時間(フレーム11から15)は29.152秒、3回目と4回目(フレーム15と19)の間は25.8秒であり、STCPよりもはるかに高速である。
これを防ぐにはどうすればよいですか?
最善の解決策は、OpenVOS 17.1 にアップグレードし、POSIX ライブラリを使用してアプリケーションを再コンパイルすることです。これにより、STCP がアプリケーションの accept 呼び出しを待たずに直ちに接続確認を送信するため、問題が発生しなくなります。ただし、アプリケーションの他の動作が変更される可能性があるため、アプリケーションの入念なテストを実施する必要があります。アップグレードと再コンパイルが不可能な場合は、
- 接続確認応答をドロップしているステートフルネットワークデバイスを再構成し、リセット応答を返すように設定します。STCPがリセットを受信すると、バックログキュー内の次の接続要求処理に移行します。これにより、ステートフルデバイスによってタイムアウトした接続要求がキューから迅速に排出されます。以降の接続確認応答はクライアントが処理し、クライアントは自身でリセットを送信するか、またはそれらを承認します。
- サーバーのバックログキューが空になるまで、クライアントが接続要求を行うのを停止する。
- サーバーアプリケーションのリスニングソケットを閉じ、再度開きます。通常、アプリケーションはこれを実行するよう設計されていないため、サーバーアプリケーションを停止して再起動する必要があります。
- syn_rcvd_abort の値を、クライアントのタイムアウト値よりも短い値に設定してください。この値は、analyze_system リクエストの set_stcp_param で変更されます。
analyze_system -request_line 'set_stcp_param syn_rcvd_abort 15' -quit OpenVOS Release 17.0.2au, analyze_system Release 17.0.2au 現在のプロセスは 151、ptep 91530AC0、Noah_Davids.CAC tcp SYN_RCVD タイムアウト (syn_rcvd_abort) を off から 15 に変更中 準備完了 11:07:44上記の例ではタイムアウトは15秒に設定されています。有効な値は1から180の間で、0を指定するとデフォルト値(約100秒)が使用されます。
これにより、単一のクライアントがバックログキューに大量の蓄積を生成するのを防ぎます。この値をどの程度短くすべきかは、同時にサーバーにアクセスする可能性のあるクライアント数によって異なります。このアプローチの問題点は、すべてのクライアントとすべてのサーバーポートに影響を与えることです。ポートのセットごとに異なる値を設定することはできません。さらに、値が低すぎると、高遅延リンクを経由する一部の接続が失敗する可能性があります。タイマーが長ければ成功したかもしれない接続です。 最適な値の選択は、科学というよりむしろ技術的な判断である
