Tiempo de espera de Apache HttpClient

7 minutos de lectura

avatar de usuario
rosty kerei

¿Hay alguna manera de especificar un tiempo de espera para toda la ejecución de HttpClient?

He probado lo siguiente:

httpClient.getParams().setParameter("http.socket.timeout", timeout * 1000);
httpClient.getParams().setParameter("http.connection.timeout", timeout * 1000);
httpClient.getParams().setParameter("http.connection-manager.timeout", new Long(timeout * 1000));
httpClient.getParams().setParameter("http.protocol.head-body-timeout", timeout * 1000);

En realidad, funciona bien, excepto si un host remoto devuelve datos, incluso a un byte/segundo, ¡continuará leyendo para siempre! Pero quiero interrumpir la conexión en 10 segundos como máximo, ya sea que el host responda o no.

  • Consulte stackoverflow.com/a/20971271/236743

    – Dori

    7 de enero de 2014 a las 12:09

  • La respuesta de @RealMan funcionó para mí.

    – adiós

    18 de abril de 2015 a las 13:24

  • Si es asíncrono, hay un tiempo de espera. Pero está sincronizado, así que de ninguna manera según en el HttpClient camino.

    – WesternGun

    14 de marzo de 2019 a las 16:51


avatar de usuario
Hombre real

Para una versión más nueva de httpclient (por ejemplo, componentes http 4.3 – https://hc.apache.org/httpcomponents-client-4.3.x/index.html):

int CONNECTION_TIMEOUT_MS = timeoutSeconds * 1000; // Timeout in millis.
RequestConfig requestConfig = RequestConfig.custom()
    .setConnectionRequestTimeout(CONNECTION_TIMEOUT_MS)
    .setConnectTimeout(CONNECTION_TIMEOUT_MS)
    .setSocketTimeout(CONNECTION_TIMEOUT_MS)
    .build();

HttpPost httpPost = new HttpPost(URL);
httpPost.setConfig(requestConfig);

  • Se agregaron las unidades, odio cuando solo dice setTimeoutdebiera ser setTimeoutMses de hecho milisegundos.

    – Christophe Roussy

    3 de marzo de 2016 a las 9:57


  • ¿Esta configuración no permitiría un ciclo de vida de solicitud total > CONNECTION_TIMEOUT_MS? Ha especificado el mismo tiempo de espera en varias etapas de la solicitud.

    – Todd liberado

    28 de marzo de 2016 a las 19:47

  • No creo que esta sea la respuesta correcta a la pregunta original. La documentación para los estados de tiempo de espera de solicitud de conexión. Devuelve el tiempo de espera en milisegundos utilizado al solicitar una conexión del administrador de conexión esto es NO el tiempo total de ejecución de la solicitud solo para obtener la conexión del administrador de conexión. Por lo tanto, si el servidor devolvía 1 btye/s, como pide el OP, esto podría superar fácilmente el tiempo de espera de la solicitud de conexión.

    – mR_fr0g

    23 mayo 2016 a las 12:50

  • De acuerdo, esta no es una solución para el problema dado. Si la solicitud continúa leyendo paquetes del socket, la solicitud nunca se agotará. La pregunta era sobre el tiempo de espera difícil.

    – Valchkou

    10 abr 2019 a las 21:39


Actualmente no hay manera de establecer un duración máxima de la solicitud de ese tipo: básicamente quieres decir No me importa si alguna etapa de solicitud específica se agota o no, pero la solicitud completa no debe durar más de 15 segundos. (por ejemplo).

Su mejor opción sería ejecutar un temporizador separado y, cuando expire, buscar el administrador de conexión utilizado por la instancia de HttpClient y cerrar la conexión, lo que debería terminar el enlace. Déjame saber si eso funciona para ti.

  • Sí, probablemente tendrá que hacer eso: el temporizador debería ejecutarse en un hilo por sí mismo.

    – Femi

    20 de julio de 2011 a las 17:25

  • ¿Vale la pena una solicitud de función? Parece un caso de uso común.

    – Chad Van De Hey

    4 abr 2019 a las 22:57

  • Aquí encontrará una implementación de dicha solución, consulte baeldung.com/httpclient-timeout#hard_timeout

    – sam

    17/03/2021 a las 15:20

Funciona bien, como propone Femi. ¡Gracias!

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    public void run() {
        if(getMethod != null) {
            getMethod.abort();
        }
    }
}, timeout * 1000);

  • necesita un candado, para evitar NPE. ¡getMethod puede volverse nulo entre la verificación y la llamada para abortar!

    – Tony BenBrahim

    27 de junio de 2012 a las 5:44

  • @TonyBenBrahim, ¿puede mostrar un ejemplo de cómo agregar este candado?

    -Tony Chan

    19 de septiembre de 2012 a las 20:04

  • @Turbo: sincronizado(foo){ … getMethod=null; } sincronizado(foo){ if (getMethod!=null){ getMethod.abort(); } }

    – Tony BenBrahim

    21 de septiembre de 2012 a las 1:24

  • @TonyBenBrahim gracias por eso. Soy nuevo en sincronización. la sección de getMethod=null; es sólo un ejemplo del método en el que el HttpClient se esta usando correctamente? Eso es, getMethod no se establecería deliberadamente en nullpero podría convertirse null cuando ese método sale, por lo tanto, debe sincronizarse (en el mismo objeto) como el Timer hilo.

    -Tony Chan

    28 de septiembre de 2012 a las 3:42


  • Esta solución no es adecuada para prod. Tenga en cuenta que cada temporizador girará un hilo. 1000/req por minuto girará 1000 hilos por minuto. Esto continuará acumulándose hasta que el servicio muera debido a la falta de memoria.

    – Valchkou

    10/04/2019 a las 21:50

avatar de usuario
Valchkou

¡El temporizador es malvado! Usar un temporizador o un ejecutor o cualquier otro mecanismo que cree un subproceso/objeto ejecutable por solicitud es una muy mala idea. Piensa sabiamente y no lo hagas. De lo contrario, se encontrará rápidamente con todo tipo de problemas de memoria con un entorno más o menos real. Imagine 1000 req/min significa 1000 subprocesos o trabajadores/min. Pobre GC. La solución que propongo requiere solo 1 hilo de vigilancia y le ahorrará recursos, tiempo y nervios. Básicamente haces 3 pasos.

  1. poner solicitud en caché.
  2. eliminar la solicitud de la memoria caché cuando se complete.
  3. cancelar las solicitudes que no estén completas dentro de su límite.

su caché junto con el hilo de vigilancia puede verse así.

import org.apache.http.client.methods.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;

public class RequestCache {

private static final long expireInMillis = 300000;
private static final Map<HttpUriRequest, Long> cache = new ConcurrentHashMap<>();
private static final ScheduledExecutorService exe = Executors.newScheduledThreadPool(1);

static {
    // run clean up every N minutes
    exe.schedule(RequestCache::cleanup, 1, TimeUnit.MINUTES);
}

public static void put(HttpUriRequest request) {
    cache.put(request, System.currentTimeMillis()+expireInMillis);
}

public static void remove(HttpUriRequest request) {
    cache.remove(request);
}

private static void cleanup() {
    long now = System.currentTimeMillis();
    // find expired requests
    List<HttpUriRequest> expired = cache.entrySet().stream()
            .filter(e -> e.getValue() > now)
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());

    // abort requests
    expired.forEach(r -> {
        if (!r.isAborted()) {
            r.abort();
        }
        cache.remove(r);
      });
    }
  }

y el siguiente código sudo cómo usar el caché

import org.apache.http.client.methods.*;

public class RequestSample {

public void processRequest() {
    HttpUriRequest req = null;
    try {
        req = createRequest();

        RequestCache.put(req);

        execute(req);

    } finally {
        RequestCache.remove(req);
    }
  }
}

A partir de las otras respuestas, mi solución fue usar un HttpRequestInterceptor para agregar el ejecutable abortar a cada solicitud. También cambié el Timer para ScheduledExecutorService.

public class TimeoutInterceptor implements HttpRequestInterceptor {

private int requestTimeout = 1 * DateTimeConstants.MILLIS_PER_MINUTE;

private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

public TimeoutInterceptor() {  }

public TimeoutInterceptor(final int requestTimeout) {
    this.requestTimeout = requestTimeout;
}

@Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
    if (request instanceof AbstractExecutionAwareRequest) {
        final AbstractExecutionAwareRequest abortableRequest = (AbstractExecutionAwareRequest) request;
        setAbort(abortableRequest);
    } else if (request instanceof HttpRequestWrapper) {
        HttpRequestWrapper wrapper = (HttpRequestWrapper) request;
        this.process(wrapper.getOriginal(), context);
    }

}

/**
 * @param abortableRequest
 */
private void setAbort(final AbstractExecutionAwareRequest abortableRequest) {
    final SoftReference<AbstractExecutionAwareRequest> requestRef = new SoftReference<AbstractExecutionAwareRequest>(abortableRequest);

    executorService.schedule(new Runnable() {

        @Override
        public void run() {
            AbstractExecutionAwareRequest actual = requestRef.get();
            if (actual != null && !actual.isAborted()) {
                actual.abort();
            }
        }
    }, requestTimeout, TimeUnit.MILLISECONDS);

}

public void setRequestTimeout(final int requestTimeout) {
    this.requestTimeout = requestTimeout;
}
}

  • Esta es una solución funcional, pero necesita crear un ejecutable por solicitud. Eso rápidamente se convierte en un problema en entornos de alto volumen real. 1000 req por min darán como resultado 1000 ejecutables por min. Los ejecutables se pondrán en cola. De hecho, se encontrará con varios problemas que causarán fallas en el servicio.

    – Valchkou

    10 abr 2019 a las 21:51


avatar de usuario
MADHAIYAN M

En la versión HttpClient 4.3, puede usar el siguiente ejemplo … digamos durante 5 segundos

int timeout = 5;
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("http://localhost:8080/service"); // GET Request
response = client.execute(request);

  • Esta es una solución funcional, pero necesita crear un ejecutable por solicitud. Eso rápidamente se convierte en un problema en entornos de alto volumen real. 1000 req por min darán como resultado 1000 ejecutables por min. Los ejecutables se pondrán en cola. De hecho, se encontrará con varios problemas que causarán fallas en el servicio.

    – Valchkou

    10 abr 2019 a las 21:51


avatar de usuario
AlexB

Eso también es trabajo:

HttpClient client = new DefaultHttpClient();
HttpConnectionParams.setConnectionTimeout(client.getParams(), timeout * 1000);
HttpConnectionParams.setSoTimeout(client.getParams(), timeout * 1000);

  • en realidad, eso es exactamente lo que hace en la pregunta, por lo que obviamente no funciona para él (ni para mí, lamentablemente, tampoco).

    – Decano Hiller

    04/10/2013 a las 18:05

¿Ha sido útil esta solución?