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