Apache HttpClient超时

Ros*_*rei 50 java timeout apache-httpclient-4.x

有没有办法为整个执行指定超时HttpClient

我尝试过以下方法:

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);
Run Code Online (Sandbox Code Playgroud)

它实际上工作正常,除非远程主机发回数据 - 即使在一个字节/秒 - 它将继续永远读取!但我想在10秒内中断连接,无论主机是否响应.

Rea*_*Man 53

对于较新版本的httpclient(例如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);
Run Code Online (Sandbox Code Playgroud)

  • 我认为这不是原始问题的正确答案.连接请求超时的文档说明._Returns从连接管理器请求连接时使用的超时(以毫秒为单位)_这不是**执行请求的总时间,只是为了从连接管理器获取连接.因此,如果服务器返回1 btye/s,则OP询问这可能很容易超过连接请求超时. (19认同)
  • 添加单位,我讨厌它只是说`setTimeout`,应该是`setTimeoutMs`,它确实是毫秒. (3认同)
  • 同意,这不是给定问题的解决方案。如果请求不断从套接字读取数据包,则请求将永远不会超时。问题是关于硬超时。 (3认同)
  • 此设置不会允许总请求生命周期> CONNECTION_TIMEOUT_MS吗?您已在请求的多个阶段指定了相同的超时. (2认同)

Fem*_*emi 32

目前无法设置该类型的最大请求持续时间:基本上您想说我不关心任何特定请求阶段是否超时,但整个请求的持续时间不得超过15秒(例如) .

最好的办法是运行一个单独的计时器,当它到期时,获取HttpClient实例使用的连接管理器并关闭连接,这应该终止链接.如果这对您有用,请告诉我.

  • 是的,可能必须这样做:Timer应该自己在一个线程中运行. (2认同)
  • 这值得提出功能请求吗?这似乎是一个常见的用例 (2认同)
  • 在这里您可以找到此类解决方案的实现,请参阅https://www.baeldung.com/httpclient-timeout#hard_timeout (2认同)

Ros*_*rei 13

按照Femi的建议,工作正常.谢谢!

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

  • 需要锁定,以避免NPE.getMethod可以在检查和中止调用之间变为空! (6认同)
  • 此解决方案不适合生产。请记住,每个计时器都会旋转一个线程。每分钟 1000/req 将每分钟旋转 1000 个线程。这将继续堆积,直到您因内存不足而死 (3认同)
  • @Turbo:synchronized(foo){ ... getMethod=null; } synchronized(foo){ if (getMethod!=null){ getMethod.abort(); } } (2认同)

Val*_*kou 10

定时器是邪恶的!使用计时器或执行程序或任何其他机制为每个请求创建一个线程/可运行对象是一个非常糟糕的主意。请慎重考虑,不要这样做。否则,您将很快在或多或少的真实环境中遇到各种内存问题。想象一下 1000 req/min 意味着 1000 个线程或工人/分钟。可怜的GC。我提出的解决方案只需要 1 个看门狗线程,将节省您的资源时间和精力。基本上你做3个步骤。

  1. 将请求放入缓存中。
  2. 完成后从缓存中删除请求。
  3. 中止在您的限制内未完成的请求。

您的缓存和看门狗线程可能如下所示。

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);
      });
    }
  }
Run Code Online (Sandbox Code Playgroud)

以及以下 sudo 代码如何使用缓存

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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)