タイムアウトのハウツー

はじめに

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

すべてのタイムアウトは、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_timeoutまたはprepost_timeoutを適切な正の値に設定する必要がありました。

低レベル TCP タイムアウト

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

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

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

一般的な接続確立の場合には、socket_connect_timeoutを使用できます。これはミリ秒単位の値を取り、socket_timeoutがサポートされていない場合でも、ほとんどのプラットフォームで機能します。接続確立中の障害検出には、TCP再送信のために数分かかる場合があるため、socket_connect_timeoutを使用することをお勧めします。ネットワークの品質に応じて、1000ミリ秒から5000ミリ秒程度のタイムアウトが適切です。socket_timeoutは秒単位であり、socket_connect_timeoutはミリ秒単位であることに注意してください。

接続プールとアイドルタイムアウト

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

ほとんどのアプリケーションでは、1日の時間や1か月の曜日によって負荷が変動します。接続プールが拡大する他の理由としては、バックエンドの一時的な遅延が挙げられ、Webサーバーなどのフロントエンドの輻輳が増加します。多くのバックエンドは、処理する着信接続ごとに専用のスレッドを使用します。したがって、通常は、負荷が減少した場合に接続プールを縮小したいと考えます。

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接続は、タイムアウトが経過した直後に閉じられるわけではありません。代わりに、すべての接続のアイドル状態をチェックする自動内部メンテナンスタスクが60秒ごとに実行されます。60秒間隔は、グローバル属性worker.maintainで調整できます。多くの副作用があるため、この値を変更することはお勧めしません。バージョン1.2.26までは、メンテナンスタスクはリクエストが処理される場合にのみ実行されます。したがって、Webサーバーに長期間リクエストを受信しないプロセスがある場合、そのプール内のアイドル接続を閉じる方法はありません。バージョン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の場合、この値を、1つのWebサーバープロセスがバックエンドに並行して送信できるリクエストの数に調整することを強くお勧めします。パフォーマンスの問題なしにピーク時に必要な接続数を測定し、成長率などに応じて、ある程度の割合を追加する必要があります。最後に、Webサーバープロセスが、プールサイズとして構成した数以上のスレッドを使用できるかどうかを確認する必要があります。

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

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

アイドル接続に関する特定の問題の1つは、Webサーバーレイヤーとバックエンドの間によくデプロイされるファイアウォールから発生します。構成によっては、アイドル状態が長すぎると、ステータステーブルから接続をサイレントに削除します。

JKおよびWebサーバーの観点からすると、相手側は単にトラフィックに応答しません。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 が応答を待機するのを中断した場合、バックエンドでの処理を停止する方法はありません。Webサーバー内の処理リソースは解放されますが、リクエストはバックエンドで実行され続けます。応答タイムアウトが発生した場合、結果を返す方法はありません。

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

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

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

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

ローカルエラー状態とグローバルエラー状態

ロードバランサーワーカーは、負荷分散の機能だけでなく、エラーが発生した場合のリクエストのセッション維持やフェイルオーバーも処理します。ロードバランサーがメンバーのいずれかでエラーを検出した場合、エラーが重大なものなのか、一時的なエラーなのか、または処理された実際のリクエストに関連しているだけなのかを判断する必要があります。一時的なエラーはローカルエラーと呼ばれ、重大なエラーはグローバルエラーと呼ばれます。

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

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

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

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

バージョン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 をアクティブにする必要があります。