Eine Kurzanleitung zu Zeitüberschreitungen in OkHttp

1. Übersicht

In diesem kurzen Tutorial konzentrieren wir uns auf verschiedene Arten von Zeitüberschreitungen, die wir für den OkHttp-Client festlegen können.

Eine allgemeinere Übersicht über die OkHttp-Bibliothek finden Sie in unserem einführenden OkHttp-Handbuch.

2. Timeout verbinden

Ein Verbindungszeitlimit definiert einen Zeitraum, in dem unser Client eine Verbindung mit einem Zielhost herstellen soll .

Standardmäßig ist für den OkHttpClient dieses Zeitlimit auf 10 Sekunden festgelegt .

Wir können den Wert jedoch leicht mit der Methode OkHttpClient.Builder # connectTimeout ändern . Ein Wert von Null bedeutet überhaupt keine Zeitüberschreitung.

Lassen Sie uns nun sehen, wie Sie einen OkHttpClient mit einem benutzerdefinierten Verbindungszeitlimit erstellen und verwenden :

@Test public void whenConnectTimeoutExceeded_thenSocketTimeoutException() { OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.MILLISECONDS) .build(); Request request = new Request.Builder() .url("//203.0.113.1") // non routable address .build(); Throwable thrown = catchThrowable(() -> client.newCall(request).execute()); assertThat(thrown).isInstanceOf(SocketTimeoutException.class); }

Das obige Beispiel zeigt, dass der Client eine SocketTimeoutException auslöst, wenn der Verbindungsversuch das konfigurierte Zeitlimit überschreitet.

3. Lesen Sie Timeout

Ein Lesezeitlimit wird angewendet, sobald die Verbindung zwischen einem Client und einem Zielhost erfolgreich hergestellt wurde.

Es definiert eine maximale Inaktivitätszeit zwischen zwei Datenpaketen, wenn auf die Antwort des Servers gewartet wird .

Das Standardzeitlimit von 10 Sekunden kann mit OkHttpClient.Builder # readTimeout geändert werden . Analog zum Verbindungszeitlimit gibt ein Nullwert kein Zeitlimit an.

Lassen Sie uns nun sehen, wie Sie in der Praxis ein benutzerdefiniertes Lesezeitlimit konfigurieren:

@Test public void whenReadTimeoutExceeded_thenSocketTimeoutException() { OkHttpClient client = new OkHttpClient.Builder() .readTimeout(10, TimeUnit.MILLISECONDS) .build(); Request request = new Request.Builder() .url("//httpbin.org/delay/2") // 2-second response time .build(); Throwable thrown = catchThrowable(() -> client.newCall(request).execute()); assertThat(thrown).isInstanceOf(SocketTimeoutException.class); }

Wie wir sehen können, gibt der Server die Antwort nicht innerhalb des definierten Zeitlimits von 500 ms zurück. Infolgedessen löst der OkHttpClient eine SocketTimeoutException aus.

4. Timeout schreiben

Ein Schreibzeitlimit definiert eine maximale Inaktivitätszeit zwischen zwei Datenpaketen beim Senden der Anforderung an den Server.

In ähnlicher Weise können wir für die Zeitüberschreitungen beim Verbinden und Lesen den Standardwert von 10 Sekunden mit OkHttpClient.Builder # writeTimeout überschreiben . Konventionell bedeutet ein Nullwert überhaupt keine Zeitüberschreitung.

Im folgenden Beispiel legen wir ein sehr kurzes Schreibzeitlimit von 10 ms fest und senden einen 1-MB-Inhalt an den Server:

@Test public void whenWriteTimeoutExceeded_thenSocketTimeoutException() { OkHttpClient client = new OkHttpClient.Builder() .writeTimeout(10, TimeUnit.MILLISECONDS) .build(); Request request = new Request.Builder() .url("//httpbin.org/delay/2") .post(RequestBody.create(MediaType.parse("text/plain"), create1MBString())) .build(); Throwable thrown = catchThrowable(() -> client.newCall(request).execute()); assertThat(thrown).isInstanceOf(SocketTimeoutException.class); }

Wie wir sehen, kann unser Client aufgrund der großen Nutzlast innerhalb des festgelegten Zeitlimits keinen Anforderungshauptteil an den Server senden. Folglich löst der OkHttpClient eine SocketTimeoutException aus .

5. Timeout aufrufen

Ein Anruf-Timeout unterscheidet sich ein wenig von den bereits besprochenen Zeitlimits für das Verbinden, Lesen und Schreiben.

Es definiert ein Zeitlimit für einen vollständigen HTTP-Aufruf . Dies umfasst das Auflösen von DNS, das Verbinden, das Schreiben des Anforderungshauptteils, die Serververarbeitung sowie das Lesen des Antworthauptteils.

Im Gegensatz zu anderen Zeitüberschreitungen wird der Standardwert auf Null gesetzt, was keine Zeitüberschreitung impliziert . Natürlich können wir einen benutzerdefinierten Wert mit der Methode OkHttpClient.Builder # callTimeout konfigurieren .

Sehen wir uns ein praktisches Anwendungsbeispiel an:

@Test public void whenCallTimeoutExceeded_thenInterruptedIOException() { OkHttpClient client = new OkHttpClient.Builder() .callTimeout(1, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("//httpbin.org/delay/2") .build(); Throwable thrown = catchThrowable(() -> client.newCall(request).execute()); assertThat(thrown).isInstanceOf(InterruptedIOException.class); }

Wie wir sehen können, wird das Anrufzeitlimit überschritten und der OkHttpClient löst eine InterruptedIOException aus.

6. Zeitlimit pro Anforderung

Es wird empfohlen, eine einzelne OkHttpClient- Instanz zu erstellen und für alle HTTP-Aufrufe in unserer Anwendung wiederzuverwenden .

Manchmal wissen wir jedoch, dass eine bestimmte Anfrage länger dauert als alle anderen. In dieser Situation müssen wir ein bestimmtes Zeitlimit nur für diesen bestimmten Anruf verlängern .

In solchen Fällen können wir eine OkHttpClient # newBuilder- Methode verwenden. Dadurch wird ein neuer Client erstellt, der dieselben Einstellungen verwendet. Wir können dann die Builder-Methoden verwenden, um die Timeout-Einstellungen nach Bedarf anzupassen.

Lassen Sie uns nun sehen, wie dies in der Praxis funktioniert:

@Test public void whenPerRequestTimeoutExtended_thenResponseSuccess() throws IOException { OkHttpClient defaultClient = new OkHttpClient.Builder() .readTimeout(1, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("//httpbin.org/delay/2") .build(); Throwable thrown = catchThrowable(() -> defaultClient.newCall(request).execute()); assertThat(thrown).isInstanceOf(InterruptedIOException.class); OkHttpClient extendedTimeoutClient = defaultClient.newBuilder() .readTimeout(5, TimeUnit.SECONDS) .build(); Response response = extendedTimeoutClient.newCall(request).execute(); assertThat(response.code()).isEqualTo(200); }

Wie wir sehen, konnte der defaultClient den HTTP-Aufruf aufgrund des überschrittenen Lesezeitlimits nicht abschließen.

Aus diesem Grund haben wir den ExtendedTimeoutClient erstellt, den Timeout-Wert angepasst und die Anforderung erfolgreich ausgeführt.

7. Zusammenfassung

In diesem Artikel haben wir verschiedene Zeitüberschreitungen untersucht, die wir für den OkHttpClient konfigurieren können .

Wir haben auch kurz beschrieben, wann die Zeitüberschreitungen beim Verbinden, Lesen und Schreiben während eines HTTP-Aufrufs angewendet werden.

Außerdem haben wir gezeigt, wie einfach es ist, einen bestimmten Zeitlimitwert nur für eine einzelne Anforderung zu ändern .

Wie üblich sind alle Codebeispiele auf GitHub verfügbar.