web-dev-qa-db-fra.com

Délai d'attente Apache HttpClient

Existe-t-il un moyen de spécifier un délai d'attente pour l'exécution entière de HttpClient ?

J'ai essayé ce qui suit:

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

Cela fonctionne vraiment bien, sauf si un hôte distant renvoie des données - même à un octet/seconde - il continuera à lire pour toujours! Mais je veux interrompre la connexion dans 10 secondes maximum, que l'hôte réponde ou non.

50
Rosty Kerei

Il n'y a actuellement aucun moyen de définir une durée maximale de demande de ce type: en gros, vous voulez dire peu m'importe si oui ou non toute étape de demande spécifique expire, mais la demande entière ne doit pas durer plus de 15 secondes (par exemple).

Le mieux serait d'exécuter un minuteur distinct et, à son expiration, de récupérer le gestionnaire de connexions utilisé par l'instance HttpClient et de fermer la connexion, ce qui devrait mettre fin au lien. Laissez-moi savoir si cela fonctionne pour vous.

31
Femi

Pour une version plus récente de httpclient (par exemple, les composants 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);
55
RealMan

Fonctionne très bien, comme proposé par Femi. Merci!

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

En s'appuyant sur les autres réponses, ma solution était d'utiliser un HttpRequestInterceptor pour ajouter le runnable d'abandon à chaque demande. J'ai également échangé le Timer contre un 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;
}
}
3
mR_fr0g

La minuterie est mauvaise. Utiliser une minuterie ou un exécuteur ou tout autre mécanisme qui crée un objet thread/exécutable par demande est une très mauvaise mauvaise idée. Réfléchissez bien et ne le faites pas. Sinon, vous rencontrerez rapidement des problèmes de mémoire avec un environnement plus ou moins réel. La solution que je propose ne nécessite qu'un seul fil de surveillance et vous fera gagner du temps et des nerfs. Fondamentalement, vous effectuez 3 étapes. 1. mettre la demande dans le cache 2. supprimer la demande du cache une fois terminée 3. abandonner les demandes qui ne sont pas complètes dans votre limite

votre cache avec le fil de surveillance peut ressembler à ceci.

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

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

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

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

et le code Sudo suivant comment utiliser le cache

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);
    }
  }
}
2
Valchkou

Dans la version HttpClient 4.3, vous pouvez utiliser l'exemple ci-dessous. Disons pendant 5 secondes

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);
1
MAA