HttpClient Timeout

1. Panoramica

Questo tutorial mostrerà come configurare un timeout con Apache HttpClient 4 .

Se vuoi approfondire e imparare altre cose interessanti che puoi fare con HttpClient, vai al tutorial principale di HttpClient .

2. Configurazione dei timeout prima di HttpClient 4.3

2.1. Raw String Parametri

Prima che uscisse la versione 4.3, HttpClient veniva fornito con molti parametri di configurazione e tutti questi potevano essere impostati in modo generico, simile a una mappa.

C'erano 3 parametri di timeout da configurare :

DefaultHttpClient httpClient = new DefaultHttpClient(); int timeout = 5; // seconds HttpParams httpParams = httpClient.getParams(); httpParams.setParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000); httpParams.setParameter( CoreConnectionPNames.SO_TIMEOUT, timeout * 1000); httpParams.setParameter( ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000));

2.2. API

Il più importante di questi parametri, ovvero i primi due, potrebbe anche essere impostato tramite un'API più indipendente dai tipi:

DefaultHttpClient httpClient = new DefaultHttpClient(); int timeout = 5; // seconds HttpParams httpParams = httpClient.getParams(); HttpConnectionParams.setConnectionTimeout( httpParams, timeout * 1000); // http.connection.timeout HttpConnectionParams.setSoTimeout( httpParams, timeout * 1000); // http.socket.timeout

Il terzo parametro non dispone di un setter personalizzato in HttpConnectionParams e dovrà comunque essere impostato manualmente tramite il metodo setParameter .

3. Configurare i timeout utilizzando il nuovo 4.3. Costruttore

L'API fluente e builder introdotta nella 4.3 fornisce il modo giusto per impostare i timeout ad alto livello :

int timeout = 5; RequestConfig config = RequestConfig.custom() .setConnectTimeout(timeout * 1000) .setConnectionRequestTimeout(timeout * 1000) .setSocketTimeout(timeout * 1000).build(); CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build();

Questo è il modo consigliato per configurare tutti e tre i timeout in modo leggibile e indipendente dai tipi.

4. Spiegazione delle proprietà del timeout

Ora, spieghiamo cosa significano questi vari tipi di timeout:

  • il Timeout di connessione ( http.connection.timeout ) - il tempo per stabilire la connessione con l'host remoto
  • il Socket Timeout ( http.socket.timeout ) - il tempo di attesa dei dati - dopo aver stabilito la connessione; tempo massimo di inattività tra due pacchetti di dati
  • il Connection Manager Timeout ( http.connection-manager.timeout ) - il tempo di attesa per una connessione dal connection manager / pool

I primi due parametri, la connessione e il timeout del socket, sono i più importanti. Tuttavia, l'impostazione di un timeout per ottenere una connessione è decisamente importante in scenari di carico elevato, motivo per cui il terzo parametro non dovrebbe essere ignorato.

5. Utilizzo di HttpClient

Dopo averlo configurato, ora possiamo utilizzare il client per eseguire richieste HTTP:

HttpGet getMethod = new HttpGet("//host:8080/path"); HttpResponse response = httpClient.execute(getMethod); System.out.println( "HTTP Status of response: " + response.getStatusLine().getStatusCode());

Con il client definito in precedenza, la connessione all'host scadrà in 5 secondi. Inoltre, se la connessione viene stabilita ma non vengono ricevuti dati, il timeout sarà di 5 secondi aggiuntivi .

Si noti che il timeout della connessione risulterà in un'eccezione org.apache.http.conn.ConnectTimeoutException , mentre il timeout del socket risulterà in un'eccezione java.net.SocketTimeoutException .

6. Hard Timeout

Sebbene l'impostazione dei timeout per stabilire la connessione HTTP e non ricevere dati sia molto utile, a volte è necessario impostare un timeout difficile per l'intera richiesta .

Ad esempio, il download di un file potenzialmente di grandi dimensioni rientra in questa categoria. In questo caso, la connessione potrebbe essere stabilita correttamente, i dati potrebbero essere trasmessi in modo coerente, ma dobbiamo comunque assicurarci che l'operazione non superi una determinata soglia di tempo.

HttpClient non ha alcuna configurazione che ci consenta di impostare un timeout complessivo per una richiesta; Tuttavia, fornisce funzionalità di interruzione per le richieste , quindi possiamo sfruttare quel meccanismo per implementare un semplice meccanismo di timeout:

HttpGet getMethod = new HttpGet( "//localhost:8080/httpclient-simple/api/bars/1"); int hardTimeout = 5; // seconds TimerTask task = new TimerTask() { @Override public void run() { if (getMethod != null) { getMethod.abort(); } } }; new Timer(true).schedule(task, hardTimeout * 1000); HttpResponse response = httpClient.execute(getMethod); System.out.println( "HTTP Status of response: " + response.getStatusLine().getStatusCode());

Stiamo utilizzando java.util.Timer e java.util.TimerTask per impostare una semplice attività ritardata che interrompe la richiesta HTTP GET dopo un hard timeout di 5 secondi.

7. Timeout e DNS Round Robin - Qualcosa di cui essere consapevoli

È abbastanza comune che alcuni domini più grandi utilizzino una configurazione DNS round robin, essenzialmente con lo stesso dominio mappato a più indirizzi IP . Ciò introduce una nuova sfida per un timeout contro un tale dominio, semplicemente a causa del modo in cui HttpClient proverà a connettersi a quel dominio che scade:

  • HttpClient ottiene l'elenco delle route IP a quel dominio
  • prova il primo - che va in timeout (con i timeout che configuriamo)
  • prova il secondo - anche quello scade
  • e così via …

Quindi, come puoi vedere, l'operazione complessiva non scadrà quando ce lo aspettiamo . Invece, scadrà quando tutti i percorsi possibili saranno scaduti. Inoltre, questo avverrà in modo completamente trasparente per il client (a meno che tu non abbia il tuo log configurato a livello di DEBUG).

Ecco un semplice esempio che puoi eseguire e replicare questo problema:

int timeout = 3; RequestConfig config = RequestConfig.custom(). setConnectTimeout(timeout * 1000). setConnectionRequestTimeout(timeout * 1000). setSocketTimeout(timeout * 1000).build(); CloseableHttpClient client = HttpClientBuilder.create() .setDefaultRequestConfig(config).build(); HttpGet request = new HttpGet("//www.google.com:81"); response = client.execute(request);

Noterai la logica di nuovo tentativo con un livello di registro DEBUG:

DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.212:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.google.com/173.194.34.212:81 timed out. Connection will be retried using another IP address DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.208:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.google.com/173.194.34.208:81 timed out. Connection will be retried using another IP address DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.209:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.google.com/173.194.34.209:81 timed out. Connection will be retried using another IP address //...

8. Conclusione

Questo tutorial ha discusso come configurare i vari tipi di timeout disponibili per un HttpClient . Ha anche illustrato un semplice meccanismo per l'hard timeout di una connessione HTTP in corso.

L'implementazione di questi esempi può essere trovata nel progetto GitHub.