HOWTO · Java
Java.Net.SocketTimeoutException: Tomcat での読み取りタイムアウト
今日のチュートリアルでは、java.net.SocketTimeoutException: Read timed out under Tomcat in Java の潜在的な理由と解決策について説明します。
このページの内容
Javaで java.net.SocketTimeoutException: Read timed out が報告された場合、ソケットの読み取りで、設定された待ち時間内にデータを受信できなかったことを示します。このメッセージだけでは、遅延の原因がリモートサービス、ネットワーク、アプリケーションの処理、呼び出し元が設定したタイムアウトのどれかは分かりません。まずタイムアウトした読み取り処理を特定し、その処理を制御するタイムアウトだけを調整してください。
java.net.SocketTimeoutException: Read timed out の意味
読み取りタイムアウトは、Javaのソケット読み取りがデータを待つ時間を制限します。通信の別の段階を制御する接続タイムアウトとは異なります。スタックトレースから、読み取り処理がHTTPクライアント、データベースドライバー、またはアプリケーションコードのどれに属するか分かる場合があります。その呼び出し箇所と周辺のログを使って、該当する処理を特定してください。
このメッセージだけで、Tomcatがクライアントへの応答に失敗したとは判断できません。エラーを記録したJavaプロセス自体が上流サービスから読み取り中だった可能性があり、タイムアウトした読み取りの前に相手側から一部のデータが届いていた場合もあります。利用できる場合は、スタックトレースをリクエストID、タイムスタンプ、双方のログと照合してください。
どのタイムアウトが発生しているかを特定する
例外のスタックトレースを確認し、読み取りを開始した最初のアプリケーションまたはライブラリのフレームを探します。HTTPレスポンス、データベースの結果、または別のソケットのどれを読み取っているのかを特定してください。次に、そのクライアントの設定とドキュメントで、読み取りタイムアウトまたはレスポンスタイムアウトを確認します。ブラウザーやフロントエンドのタイムアウトが関係するのは、タイムアウトした読み取りが実際にそのクライアントによって制御されている場合だけです。
呼び出し経路にTomcatが含まれている場合は、どのプロセスがエラーを記録し、どの接続から読み取っていたかを確認してください。外向きのJavaクライアントに設定されたタイムアウトは、Tomcat Connectorの設定とは別のものです。例外メッセージだけから、原因となった設定を推測しないでください。
該当するTomcatのタイムアウトを設定する
connectionTimeout はTomcat Connectorの設定ですが、具体的な動作や既定値は、使用しているTomcatのバージョンとコネクターによって異なります。Javaクライアントのソケット読み取りタイムアウトとは別の設定です。Tomcat 10.1 Connectorの設定ドキュメントを確認したうえで、実際に使用しているTomcatのバージョンに対応するドキュメントを参照し、その設定がスタックトレースで特定した処理に適用されることを確かめてください。ここではTomcatのバージョンが分からないため、安全に推奨できるConnector XMLの変更例はありません。
ソケットを所有するJavaクライアントでは、Socket.setSoTimeout でそのソケットの読み取りタイムアウトを設定できます。このプログラムは設定値を読み戻し、英語のラベル Read timeout: とともに表示します。
import java.io.IOException;
import java.net.Socket;
class ReadTimeoutSetup {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket()) {
socket.setSoTimeout(5000);
System.out.println("Read timeout: " + socket.getSoTimeout() + " ms");
}
}
}
Read timeout: 5000 ms
この例はサーバーに接続せず、Tomcatの設定も変更しません。クライアント側の設定は、タイムアウトした読み取りを実際に所有するコンポーネントでのみ適用し、値がそのクライアントのAPIと運用上の要件に合っていることを確認してください。
ネットワーク、サーバー、アプリケーションの遅延を調べる
呼び出し元、Tomcat、上流サービスの間で、タイムスタンプとリクエストIDを照合してください。レスポンスデータが届いていない時間帯を探し、ネットワークエラー、サーバーログ、アプリケーショントレースと比較します。データの遅延は、ネットワークの遅延、アプリケーション処理の遅さ、時間のかかるデータベースクエリ、リソースの競合、同時負荷などで起こる可能性があります。これらは調査の手がかりであり、例外だけで原因と断定できるものではありません。
同じ時間帯のCPU、メモリ、スレッド、コネクションプール、データベースのメトリクスを確認します。対象のリクエストがアプリケーションに到達したか、下流への呼び出しやクエリに通常より時間がかかったかも調べてください。サービスがプロキシやロードバランサーの背後にある場合は、そのタイムアウト設定とログをクライアントおよびサーバーの時系列と比較します。関連する設定は一度に一つだけ変更し、遅延の原因を引き続き調べながら、該当の読み取りが完了するかを確認してください。
長時間かかるリクエストを安全に扱う
処理に同期リクエストの待ち時間を超える時間が正当に必要な場合は、非同期処理への移行を検討してください。処理を受け付けたことを示すレスポンスとジョブIDを返し、処理は別途実行して、クライアントが完了を確認できるようにします。これにより処理が終わるまでリクエストを開いたままにせずに済みますが、アプリケーション側の対応と、失敗や進捗を明確に伝える方法が必要です。
待ち時間を延ばすのが適切な場合は、特定した読み取りを制御するタイムアウトだけを延長し、関連するクライアント、プロキシ、サーバーの制限に矛盾がないようにしてください。待ち時間を過度に長くすると、スレッド、ソケット、その他のリソースを占有し、サービスの遅延を見えにくくする可能性があります。実測したリクエスト時間と処理能力に基づいて値を決め、完了率とリソース使用量の両方を監視してください。