Una guida rapida ai timeout in OkHttp

1. Panoramica

In questo breve tutorial, ci concentreremo sui diversi tipi di timeout che possiamo impostare per il client OkHttp.

Per una panoramica più generale della libreria OkHttp, consultare la nostra guida introduttiva a OkHttp.

2. Connetti Timeout

Un timeout di connessione definisce un periodo di tempo in cui il nostro client deve stabilire una connessione con un host di destinazione .

Per impostazione predefinita, per OkHttpClient , questo timeout è impostato su 10 secondi .

Tuttavia, possiamo facilmente modificare il suo valore utilizzando il metodo OkHttpClient.Builder # connectTimeout . Un valore pari a zero significa nessun timeout.

Vediamo ora come creare e utilizzare un OkHttpClient con un timeout di connessione personalizzato:

@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); }

L'esempio precedente mostra che il client genera un'eccezione SocketTimeoutException quando il tentativo di connessione supera il timeout configurato.

3. Leggi Timeout

Viene applicato un timeout di lettura dal momento in cui la connessione tra un client e un host di destinazione è stata stabilita con successo.

Definisce un tempo massimo di inattività tra due pacchetti di dati in attesa della risposta del server .

Il timeout predefinito di 10 secondi può essere modificato utilizzando OkHttpClient.Builder # readTimeout . Analogamente al timeout di connessione, un valore zero indica l'assenza di timeout.

Vediamo ora come configurare in pratica un timeout di lettura personalizzato:

@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); }

Come possiamo vedere, il server non restituisce la risposta entro il timeout definito di 500 ms. Di conseguenza, OkHttpClient genera un'eccezione SocketTimeoutException.

4. Scrivi Timeout

Un timeout di scrittura definisce un tempo massimo di inattività tra due pacchetti di dati quando si invia la richiesta al server.

Allo stesso modo, come per i timeout di connessione e lettura, possiamo sovrascrivere il valore predefinito di 10 secondi utilizzando OkHttpClient.Builder # writeTimeout . Per convenzione, un valore zero significa nessun timeout.

Nell'esempio seguente, impostiamo un timeout di scrittura molto breve di 10 ms e inviamo un contenuto di 1 MB al 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); }

Come si vede, a causa del grande carico utile, il nostro client non è in grado di inviare un corpo di richiesta al server entro il timeout definito. Di conseguenza, OkHttpClient genera un'eccezione SocketTimeoutException .

5. Call Timeout

Un timeout di chiamata è leggermente diverso dai timeout di connessione, lettura e scrittura di cui abbiamo già parlato.

Definisce un limite di tempo per una chiamata HTTP completa . Ciò include la risoluzione del DNS, la connessione, la scrittura del corpo della richiesta, l'elaborazione del server e la lettura del corpo della risposta.

A differenza di altri timeout, il suo valore predefinito è impostato su zero, il che non implica alcun timeout . Ma ovviamente, possiamo configurare un valore personalizzato utilizzando il metodo OkHttpClient.Builder # callTimeout .

Vediamo un esempio pratico di utilizzo:

@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); }

Come possiamo vedere, il timeout della chiamata viene superato e OkHttpClient genera un'eccezione interrottaIOException.

6. Timeout per richiesta

Si consiglia di creare una singola istanza OkHttpClient e riutilizzarla per tutte le chiamate HTTP nella nostra applicazione.

A volte, tuttavia, sappiamo che una determinata richiesta richiede più tempo di tutte le altre. In questa situazione, dobbiamo estendere un determinato timeout solo per quella particolare chiamata .

In questi casi, possiamo usare un metodo OkHttpClient # newBuilder . Questo crea un nuovo client che condivide le stesse impostazioni. Possiamo quindi utilizzare i metodi del builder per regolare le impostazioni di timeout secondo necessità.

Vediamo ora come farlo in pratica:

@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); }

Come vediamo il defaultClient non è riuscito a completare la chiamata HTTP a causa del timeout di lettura superato.

Ecco perché abbiamo creato extendedTimeoutClient, modificato il valore di timeout ed eseguito correttamente la richiesta.

7. Riepilogo

In questo articolo, abbiamo esplorato diversi timeout che possiamo configurare per OkHttpClient .

Abbiamo anche descritto brevemente quando i timeout di connessione, lettura e scrittura vengono applicati durante una chiamata HTTP.

Inoltre, abbiamo mostrato quanto sia facile modificare un determinato valore di timeout solo per una singola richiesta .

Come al solito, tutti gli esempi di codice sono disponibili su GitHub.