タイムアウトHowTo

はじめに

通信プロセスを改善するためには、通信タイムアウトの設定が非常に重要です。これらは問題の検出と分散システムの安定化に役立ちます。JKは、個別に設定できるいくつかの異なるタイムアウトタイプを使用できます。歴史的な理由により、これらはすべてデフォルトで無効になっています。このHowToでは、それらの使用法と適切な値を見つけるためのヒントを説明します。

すべてのタイムアウトは、workers.properties ファイルで設定できます。すべてのワーカー設定項目の完全なリファレンスについては、ワーカーのリファレンスを参照してください。このページは、JKの少なくともバージョン1.2.16を使用していることを前提としています。新しいバージョンへの依存関係は、必要に応じて言及されます。

タイムアウトを極端な値に設定しないでください。非常に小さなタイムアウトは、逆効果になる可能性があります。

バックエンドでの長いガベージコレクションの一時停止は、一部のタイムアウトとうまく適合しません。JavaのメモリとGC設定を最適化するようにしてください。

JKタイムアウト属性

CPing/CPong

CPing/CPongは、バックエンド接続の状態を確認するために小さなテストパケットを使用するという私たちの概念です。JKは、新しいバックエンド接続を確立した直後(接続モード)および各リクエストをバックエンドに送信する直前(プリポストモード)に、このようなテストパケットを使用できます。バージョン1.2.27からは、接続が長時間アイドル状態であった場合(インターバルモード)にも使用できます。CPingへのCPong応答の最大待機時間(タイムアウト)と、インターバルモードでのアイドル時間を設定できます。

テストパケットは、最小限の処理リソースでバックエンドによって非常に迅速に回答されます。肯定的な回答は、バックエンドに到達可能であり、アクティブにリクエストを処理していることを示します。これにより、何らかのコンテキストがデプロイされ機能しているかどうかは検出されません。CPing/CPongの利点は、バックエンドとの通信問題を迅速に検出できることです。欠点は、わずかにレイテンシが増加することです。

ワーカー属性ping_modeは、テストパケットが使用される状況を決定するために、文字の組み合わせに設定できます。

  • C: 接続モード、タイムアウトping_timeoutconnect_timeoutで上書きされます
  • P: プリポストモード、タイムアウトping_timeoutprepost_timeoutで上書きされます
  • I: インターバルモード、タイムアウトping_timeout、アイドル時間connection_ping_interval
  • A: 全モード

複数の値は、区切り文字なしで連結する必要があります。すべてのCPingテストを使用することをお勧めします。アプリケーションが非常にレイテンシに敏感な場合は、接続モードとインターバルモードの組み合わせのみを使用する必要があります。

ping_modeによるCPingプローブの有効化はバージョン1.2.27で追加されました。それ以前のバージョンでは、接続モードとプリポストモードのみが存在し、connect_timeoutprepost_timeoutを明示的に設定することで有効にする必要がありました。

ワーカー属性ping_timeoutは、すべてのモードにおけるCPongのデフォルト待機タイムアウトをミリ秒で設定します。デフォルト値は「10000」ミリ秒です。この値は、ping_modeを介してCPing/CPongプローブを有効にした場合にのみ使用されます。Javaのガベージコレクションが非常に長時間一時停止する場合を除き、デフォルト値で問題ありません。ネットワークのレイテンシと安定性にもよりますが、適切なカスタム値は通常5000ミリ秒から15000ミリ秒の間です。接続モードとプリポストモードで使用されるタイムアウトは、connect_timeoutprepost_timeoutで上書きできます。注意:極端に小さな値は使用しないでください。

ワーカー属性connect_timeoutは、接続確立時のCPongの待機タイムアウトをミリ秒で設定します。ping_timeoutで設定された一般的なタイムアウトを上書きしたい場合に使用できます。接続モードCPingを使用するには、ping_modeを介して有効にする必要があります。JKは通常、持続的な接続を使用するため、新しい接続を開くことは稀なイベントです。したがって、接続モードを有効にすることをお勧めします。ネットワークのレイテンシと安定性にもよりますが、適切な値は通常5000ミリ秒から15000ミリ秒の間です。注意:極端に小さな値は使用しないでください。

ワーカー属性prepost_timeoutは、リクエスト転送前のCPongの待機タイムアウトをミリ秒で設定します。ping_timeoutで設定された一般的なタイムアウトを上書きしたい場合に使用できます。プリポストモードCPingを使用するには、ping_modeを介して有効にする必要があります。このタイプのCPing/CPongを有効にすると、各リクエストにわずかなレイテンシが追加されます。通常、これは十分に小さく、CPing/CPongの利点の方が重要です。そのため、一般的にはprepost_timeoutの使用も推奨します。ネットワークのレイテンシと安定性にもよりますが、適切な値は通常5000ミリ秒から10000ミリ秒の間です。注意:極端に小さな値は使用しないでください。

バージョン1.2.27までは、ping_modeping_timeoutは存在せず、接続モードまたはプリポストモードCPingを有効にするには、それぞれconnect_timeoutprepost_timeoutを妥当な正の値に設定する必要がありました。

低レベルTCPタイムアウト

一部のプラットフォームでは、TCPソケット上のすべての操作に対してタイムアウトを設定できます。これはLinuxとWindowsで利用可能ですが、Solarisなどの他のプラットフォームではサポートされていません。プラットフォームがTCP送信および受信タイムアウトをサポートしている場合、ワーカー属性socket_timeoutを使用してそれらを設定できます。2つのタイムアウトを異なる値に設定することはできません。

JKは、プラットフォームがソケットタイムアウトをサポートしていない場合でも、この属性を受け入れます。この場合、属性を設定しても効果はありません。デフォルトでは値は「0」で、タイムアウトは無効です。この属性を秒単位の値(ミリ秒ではない)に設定できます。するとJKは、バックエンド接続の送信タイムアウトと受信タイムアウトをこの値に設定します。このタイムアウトは低レベルであり、ソケット上の各読み書き操作に個別に適用されます。

この属性を使用すると、JKは特定の種類のネットワーク問題に対してより迅速に反応するようになります。残念ながら、ソケットタイムアウトは負の副作用を伴います。なぜなら、ほとんどのプラットフォームでは、一度タイムアウトが発生すると、そこから回復する良い方法がないからです。JKには、このタイムアウトが実際のネットワーク問題のために発生したのか、それとも単にバックエンドから時間内に応答パケットを受信しなかったためなのかを判断する方法がありません。したがって、注意してください:極端に小さな値は使用しないでください。

接続確立の一般的なケースでは、socket_connect_timeoutを使用できます。これはミリ秒値をとり、socket_timeoutがサポートされていない場合でもほとんどのプラットフォームで動作します。socket_connect_timeoutの使用を推奨します。なぜなら、一部のネットワーク障害状況では、TCP再送信のために接続確立中の障害検出に数分かかる場合があるからです。ネットワークの品質にもよりますが、1000ミリ秒から5000ミリ秒の間のタイムアウトが適切でしょう。なお、socket_timeoutは秒単位であり、socket_connect_timeoutはミリ秒単位です。

コネクションプールとアイドルタイムアウト

JKは、ウェブサーバープロセスごとにコネクションプールでバックエンド接続を処理します。接続は持続モードで使用されます。リクエストが正常に完了した後、接続を開いたままにして次の転送リクエストを待ちます。コネクションプールは、並行してリクエストを転送したいスレッドの数に応じて拡張できます。

ほとんどのアプリケーションは、時刻や月の日によって負荷が変動します。コネクションプールが増大する他の理由としては、バックエンドの一時的な遅延があり、これによりウェブサーバーのようなフロントエンドの輻輳が増加します。多くのバックエンドは、処理する各着信接続に専用のスレッドを使用します。そのため、通常は負荷が減少した場合にコネクションプールが縮小することを望みます。

JKは、アイドル時間が経過した後にプール内の接続を閉じることができます。この最大アイドル時間は、秒単位で指定される属性connection_pool_timeoutで設定できます。デフォルト値は「0」で、アイドル接続のクローズを無効にします。

一般的に、約10分程度の値を推奨します。そのため、connection_pool_timeoutを600(秒)に設定します。この属性を使用する場合は、Tomcatのserver.xml設定ファイルのAJP Connector要素にある属性keepAliveTimeout(明示的に設定されている場合)またはconnectionTimeoutも同様の値に設定してください。注意keepAliveTimeoutconnectionTimeoutはミリ秒で指定する必要があります。したがって、JKのconnection_pool_timeoutを600に設定した場合、TomcatのkeepAliveTimeoutまたはconnectionTimeoutを600000に設定する必要があります。

JK接続は、タイムアウトが経過した直後に閉じられるわけではありません。代わりに、すべての接続のアイドル状態をチェックする自動的な内部メンテナンスLタスクが60秒ごとに実行されます。60秒間隔は、グローバル属性worker.maintainで調整できます。多くの副作用があるため、この値を変更することはお勧めしません。バージョン1.2.26までは、メンテナンスLタスクはリクエストが処理された場合にのみ実行されました。そのため、ウェブサーバーに長時間リクエストを受信しないプロセスがある場合、そのプール内のアイドル接続を閉じる方法はありませんでした。バージョン1.2.27以降では、Apache HTTP Server 2.xをスレッド化されたAPRまたはMicrosoft IISで使用している場合、独立した監視スレッドを設定できます。

最大コネクションプールサイズは、属性connection_pool_sizeで設定できます。一般的に、この属性をApache HTTP Serverと組み合わせて使用することはお勧めしません。Apacheの場合、プロセスあたりのスレッド数を自動的に検出し、最大プールサイズをこの値に設定します。Microsoft IISの場合、デフォルト値として250を使用します(バージョン1.2.20より前は10)。IISの場合、この値を、単一のウェブサーバープロセスがバックエンドに並行して送信できるリクエストの数に調整することを強くお勧めします。パフォーマンス上の問題なくピーク時に必要な接続数を測定し、成長率などに応じていくらかのパーセンテージを追加する必要があります。最後に、ウェブサーバープロセスがプールサイズとして設定したスレッド数以上を使用できるかどうかを確認する必要があります。

JK属性connection_pool_minsizeは、プールが縮小されたときにアイドル接続がいくつ残るかを定義します。デフォルトでは、これは最大プールサイズの半分です。

ファイアウォールによる接続切断

アイドル接続に関する特定の1つの問題は、ウェブサーバー層とバックエンドの間にしばしば配置されるファイアウォールに起因します。ファイアウォールの設定によっては、アイドル状態が長すぎると、ステータステーブルから接続をサイレントに切断します。

JKとウェブサーバーの観点からすると、相手側は単にトラフィックに応答しません。TCPは信頼性の高いプロトコルであるため、欠落したTCP ACKを検出し、比較的長時間(通常は数分間)パケットの再送信を試みます。したがって、アイドル接続の切断を防ぐために、JK側では常にconnection_pool_timeoutとconnection_pool_minsizeを、Tomcat側ではkeepAliveTimeoutまたはconnectionTimeoutを使用する必要があります。

さらに、ブール属性socket_keepaliveを使用すると、各接続で一定時間アイドル状態になった後にTCPキープアライブパケットを自動的に送信する標準ソケットオプションを設定できます。デフォルトでは、これはfalseに設定されています。ファイアウォールによるアイドル接続の切断を疑う場合は、これをtrueに設定する必要があります。

残念ながら、これらのパケットのデフォルトの間隔とアルゴリズムはプラットフォーム固有です。TCPキープアライブを制御する方法については、お使いのプラットフォームのTCPチューニングオプションを確認する必要があるかもしれません。多くの場合、デフォルトの間隔はアイドル接続に対するファイアウォールのタイムアウトよりもはるかに長いです。それでも、ファイアウォールとプラットフォームのTCPチューニングに適した設定値について合意を得るために、ファイアウォール管理者およびプラットフォーム管理者と話し合うことをお勧めします。

当社の推奨事項のいずれも役に立たず、アイドル接続の切断問題が確実にある場合は、Apache HTTP Serverと共にJKを使用する際に、持続的接続の使用を無効にすることができます。これを行うには、Apache設定で「JkOptions +DisableReuse」を設定します。これにより生じるパフォーマンスへの影響は、ネットワークとファイアウォールの詳細によって異なります。

応答タイムアウト

JKは、リクエストの応答にタイムアウトを使用することもできます。このタイムアウトは、応答の完全な処理時間を測定するものではありません。その代わりに、連続する応答パケット間の許容時間を制御します。

ほとんどの場合、これが実際に望まれることです。例えば、長時間実行されるダウンロードを考えてみてください。ダウンロードは何分も続く可能性があるため、効果的なグローバル応答タイムアウトを設定することはできません。しかし、ほとんどのアプリケーションは、応答を返し始める前に限られた処理時間しかありません。これらのアプリケーションでは、明示的な応答タイムアウトを設定できます。応答タイムアウトと調和しないアプリケーションは、バッチ型アプリケーション、データウェアハウス、および長時間の処理が予想されるレポートアプリケーションです。

応答タイムアウトが発生したためにJKが応答の待機を中止した場合、バックエンドでの処理を停止する方法はありません。ウェブサーバーの処理リソースは解放されますが、リクエストはバックエンドで実行され続け、応答タイムアウトが発生した後はいかなる結果も送り返すことはできません。

JKは、応答タイムアウトを設定するためにワーカー属性reply_timeoutを使用します。デフォルト値は「0」(タイムアウト無効)で、任意のミリ秒値を設定できます。

Apache HTTP Serverと組み合わせて使用する場合、Apache環境変数を使用してより柔軟なreply_timeoutを設定することもできます。変数JK_REPLY_TIMEOUTに整数値を設定すると、ワーカー設定の値の代わりにこの値が使用されます。これにより、URI、クエリ文字列などに応じてmod_setenvifおよびmod_rewriteで応答タイムアウトをより柔軟に設定できます。環境変数JK_REPLY_TIMEOUTが設定されていないか、負の値に設定されている場合、ワーカーのデフォルトの応答タイムアウトが使用されます。JK_REPLY_TIMEOUTに「0」の値が含まれている場合、そのリクエストに対して応答タイムアウトは無効になります。

ロードバランシングワーカーと組み合わせて使用する場合、応答タイムアウトが発生すると、JKはロードバランサーのメンバーワーカーを無効にします。そのワーカーは、次の自動メンテナンスLタスクで回復されるまで使用されなくなります。JK 1.2.24以降では、max_reply_timeoutsを使用することでこの動作を改善できます。この属性により、ワーカーを無効にすることなく、時折の長時間実行リクエストを許可します。そのようなリクエストが頻繁に発生した場合にのみ、ワーカーはロードバランサーによって無効化されます。

ロードバランサーエラー検出

ローカルおよびグローバルエラー状態

ロードバランサーワーカーは、負荷分散だけでなく、エラー発生時のリクエストのスティッキーネスとフェイルオーバーも処理します。ロードバランサーがメンバーの1つでエラーを検出した場合、そのエラーが深刻なものなのか、一時的なものなのか、あるいは処理された実際のリクエストにのみ関連するものなのかを判断する必要があります。一時的なエラーはローカルエラーと呼ばれ、深刻なエラーはグローバルエラーと呼ばれます。

ロードバランサーがバックエンドをグローバルエラー状態にするべきだと判断した場合、ウェブサーバーはそれ以上リクエストを送信しません。セッションレプリケーションが使用されていない場合、これは該当するバックエンドに存在するすべてのユーザーセッションが利用できなくなることを意味します。ユーザーは別のバックエンドに転送され、再度ログインする必要があります。したがって、グローバルエラー状態はユーザーにとって透過的ではありません。アプリケーションは引き続き利用可能ですが、ユーザーは一部の作業を失う可能性があります。

場合によっては、ローカルエラーとグローバルエラーの判断は簡単です。たとえば、クライアント(ブラウザ)に応答を返す際にエラーが発生した場合、バックエンドが壊れている可能性は非常に低いでしょう。したがって、この状況はローカルエラーの典型的な例です。

しかし、判断が難しい状況もあります。ロードバランサーがバックエンドへの新しい接続を確立できない場合、それは一時的な過負荷状況(つまり、バックエンドに空きスレッドがない)のためか、あるいはバックエンドがもう稼働していないためかもしれません。詳細によっては、正しい状態はローカルエラーまたはグローバルエラーのいずれかになります。

エラーエスカレーション時間

バージョン1.2.26までは、ほとんどのエラーがグローバルエラーとして解釈されていました。バージョン1.2.27以降では、以前はグローバルと解釈されていた多くのエラーが、バックエンドがまだビジー状態である限りローカルに切り替えられるようになりました。ビジーとは、他の同時リクエストが同じバックエンドに送信されている(成功しているか否かにかかわらず)ことを意味します。

多くの場合、ローカルエラーとグローバルエラーを完璧に判断する方法はありません。ロードバランサーは単に十分な情報を持っていません。バージョン1.2.28では、ロードバランサーがローカルエラーからグローバルエラーに切り替わる速さを調整できるようになりました。ロードバランサーのメンバーがローカルエラー状態に長く留まりすぎると、ロードバランサーはそれをグローバルエラー状態にエスカレートさせます。

ローカルエラー状態での許容時間は、ロードバランサー属性error_escalation_time(秒単位)によって制御されます。デフォルト値はrecover_timeの半分であるため、recover_timeを変更していない限り、デフォルトは30秒です。

error_escalation_timeの値を小さくすると、ロードバランサーは深刻なエラーに対してより迅速に反応しますが、それほど深刻でない状況でもセッションを失うリスクが高まります。error_escalation_timeを0秒まで下げることができ、これは潜在的に深刻なすべてのローカルエラーがすぐにグローバルエラーにエスカレートされることを意味します。

基本的なエラー検出が良好でない場合、エスカレーションプロセス全体が無駄になることに注意してください。したがって、error_escalation_timeのチューニングを考える前に、必ずsocket_connect_timeoutを使用し、ping_modeping_timeoutでCPing/CPongを有効にする必要があります。