1. 概要

このチュートリアルでは、Java11以降で利用可能な新しいJavaHTTPクライアントとJavaでタイムアウトを設定する方法を示します。

知識を更新する必要がある場合は、JavaHTTPクライアントのチュートリアルから始めることができます。

一方、古いライブラリを使用してタイムアウトを設定する方法については、HttpUrlConnection。を参照してください。

2. タイムアウトの構成

まず、HTTPリクエストを作成できるようにHttpClientを設定する必要があります。

private static HttpClient getHttpClientWithTimeout(int seconds) {
    return HttpClient.newBuilder()
      .connectTimeout(Duration.ofSeconds(seconds))
      .build();
}

上記では、パラメータとして定義されたタイムアウトで構成されたHttpClientを返すメソッドを作成しました。 間もなく、 Builderデザインパターンを使用してHttpClientをインスタンス化し、connectTimeoutメソッドを使用してタイムアウトを構成します。 さらに、静的メソッド ofSeconds を使用して、タイムアウトを秒単位で定義するDutyオブジェクトのインスタンスを作成しました。

その後、HttpClientタイムアウトが正しく構成されているかどうかを確認します。

httpClient.connectTimeout().map(Duration::toSeconds)
  .ifPresent(sec -> System.out.println("Timeout in seconds: " + sec));

したがって、connectTimeoutメソッドを使用してタイムアウトを取得します。 その結果、秒にマップした duration、Optionalが返されます。

3. タイムアウトの処理

さらに、クライアントがHTTPリクエストを作成するために使用するHttpRequestオブジェクトを作成する必要があります。

HttpRequest httpRequest = HttpRequest.newBuilder()
  .uri(URI.create("http://10.255.255.1")).GET().build();

タイムアウトをシミュレートするために、ルーティング不可能なIPアドレスを呼び出します。 つまり、すべてのTCPパケットがドロップされ、前に設定された事前定義された期間の後にタイムアウトが強制されます。

それでは、タイムアウトを処理する方法を詳しく見ていきましょう。

3.1. 同期呼び出しタイムアウトの処理

たとえば、同期呼び出しを行うには、sendメソッドを使用します。

HttpConnectTimeoutException thrown = assertThrows(
  HttpConnectTimeoutException.class,
  () -> httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()),
  "Expected send() to throw HttpConnectTimeoutException, but it didn't");
assertTrue(thrown.getMessage().contains("timed out"));

同期呼び出しは、HttpConnectTimeoutExceptionが拡張するIOExceptionを強制的にキャッチします。 したがって、上記のテストでは、HttpConnectTimeoutExceptionにエラーメッセージが表示されることが予想されます。

3.2. 非同期呼び出しタイムアウトの処理

同様に、非同期呼び出しを行うには、sendAsyncメソッドを使用します。

CompletableFuture<String> completableFuture = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())
  .thenApply(HttpResponse::body)
  .exceptionally(Throwable::getMessage);
String response = completableFuture.get(5, TimeUnit.SECONDS);
assertTrue(response.contains("timed out"));

上記の呼び出し sendAsync を返します CompletableFuture 。 したがって、応答を機能的に処理する方法を定義する必要があります。 詳細には、エラーが発生しなかった場合の応答から本文を取得します。 それ以外の場合は、throwableからエラーメッセージが表示されます。 最後に、最大5秒間待機することにより、CompleteableFutureから結果を取得します。 この場合も、このリクエストは3秒後にHttpConnectTimeoutExceptionをスローします。

4. リクエストレベルでタイムアウトを設定する

上記では、syncasyncの両方の呼び出しに同じクライアントインスタンスを再利用しました。 ただし、リクエストごとにタイムアウトを異なる方法で処理したい場合があります。 同様に、単一のリクエストのタイムアウトを設定できます。

HttpRequest httpRequest = HttpRequest.newBuilder()
  .uri(URI.create("http://10.255.255.1"))
  .timeout(Duration.ofSeconds(1))
  .GET()
  .build();

同様に、 timeout メソッドを使用して、このリクエストのタイムアウトを設定しています。 ここでは、このリクエストのタイムアウトを1秒に設定しました。

クライアントとリクエストの間の最小期間は、リクエストのタイムアウトを設定します。

5. 結論

この記事では、新しいJava HTTPクライアントを使用してタイムアウトを正常に構成し、タイムアウトがオーバーフローしたときにリクエストを適切に処理します。

そしていつものように、例のソースコードはGitHubから入手できます。