Java Apache HttpClient超时

Java Apache HttpClient超时,java,timeout,apache-httpclient-4.x,Java,Timeout,Apache Httpclient 4.x,是否有一种方法可以为整个执行指定超时 我尝试了以下方法: httpClient.getParams().setParameter(“http.socket.timeout”,timeout*1000); httpClient.getParams().setParameter(“http.connection.timeout”,timeout*1000); httpClient.getParams().setParameter(“http.connection manager.timeout”,新

是否有一种方法可以为整个执行指定超时

我尝试了以下方法:

httpClient.getParams().setParameter(“http.socket.timeout”,timeout*1000);
httpClient.getParams().setParameter(“http.connection.timeout”,timeout*1000);
httpClient.getParams().setParameter(“http.connection manager.timeout”,新长(timeout*1000));
httpClient.getParams().setParameter(“http.protocol.head-body-timeout”,timeout*1000);

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

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


您最好运行一个单独的计时器,当计时器过期时,获取HttpClient实例使用的连接管理器并关闭连接,这将终止链接。请告诉我这是否适用于您。

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

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

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

在HttpClient 4.3版本中,您可以使用下面的示例。比方说5秒钟

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

对于更新版本的httpclient(例如http组件4.3-):


在其他答案的基础上,我的解决方案是使用一个
HttpRequestInterceptor
将中止可运行添加到每个请求中。此外,我还将
计时器换成了
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;
}
}
公共类TimeoutInterceptor实现HttpRequestInterceptor{
private int requestTimeout=1*DateTimeConstants.MILLIS_/分钟;
private ScheduledExecutorService executorService=Executors.newSingleThreadScheduledExecutor();
public TimeoutInterceptor(){}
公共TimeoutInterceptor(最终int请求超时){
this.requestTimeout=requestTimeout;
}
@凌驾
公共无效进程(最终HttpRequest请求、最终HttpContext上下文)抛出HttpException、IOException{
if(AbstractExecutionAwareRequest的请求实例){
最终AbstractExecutionAwareRequest abortableRequest=(AbstractExecutionAwareRequest)请求;
setAbort(abortableRequest);
}else if(HttpRequestWrapper的请求实例){
HttpRequestWrapper=(HttpRequestWrapper)请求;
进程(wrapper.getOriginal(),context);
}
}
/**
*@param abortableRequest
*/
私有void setAbort(最终AbstractExecutionAwareRequest abortableRequest){
最终SoftReference requestRef=新的SoftReference(中止请求);
executorService.schedule(新的Runnable(){
@凌驾
公开募捐{
AbstractExecutionAwareRequest actual=requestRef.get();
if(actual!=null&!actual.isborted()){
actual.abort();
}
}
},请求超时,时间单位为毫秒);
}
public void setRequestTimeout(最终整数请求超时){
this.requestTimeout=requestTimeout;
}
}
计时器是邪恶的! 使用计时器、执行器或任何其他机制为每个请求创建线程/可运行对象是一个非常糟糕的主意。请明智地思考,不要这样做。否则,在或多或少真实的环境中,您将很快遇到各种各样的内存问题。想象一下,1000 req/min意味着1000线程或工作者/min。糟糕的GC。我提出的解决方案是require只有一个看门狗线程,将节省您的资源、时间和精力。 基本上你要做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);
          });
        }
      }
    

    是的,可能必须这样做:计时器应该自己在线程中运行。这值得一个功能请求吗?这似乎是一个常见的用例。在这里,您可以找到这样一个解决方案的实现,请参阅需要一个锁,以避免NPE。getMethod可以在检查和调用中止之间变为null!@TonyBenBrahim您可以展示一个如何添加此功能的示例吗lock?@Turbo:synchronized(foo){…getMethod=null;}synchronized(foo){if(getMethod!=null){getMethod.abort();}@TonyBenBrahim对此表示感谢。我是同步新手。
    getMethod=null;
    部分只是正确使用
    HttpClient
    的方法的一个例子?也就是说,
    getMethod
    不会故意设置为
    null
    ,但当该方法退出时,可能会变成
    null
    ,因此它是正确的应同步(在同一对象上)作为
    计时器
    线程。此解决方案不适用于产品。请记住,每个计时器将旋转一个线程。每分钟1000个/req将旋转1000个线程。这将继续累积,直到您的服务因内存不足而死亡。实际上,这正是他在问题中所做的,因此显然对他不起作用(不幸的是,我也一样)。请参阅@RealMan的答案。如果它是异步的,则会有一个超时。但它是同步的,因此无法按照
    HttpClient
    的方式进行。添加了单位,我讨厌它只说
    setTimeout
    ,应该是
    setTimeoutMs
    ,实际上是毫秒。此设置是否允许总请求生命周期>连接超时?您在请求的多个阶段指定了相同的超时。我认为这不是对原始问题的正确答案。连接请求超时的文档说明。返回从连接管理器请求连接时使用的超时(以毫秒为单位)。这不是执行请求的总时间ust从连接管理器获取连接。因此,如果服务器返回1字节/秒(如OP所要求),则很容易超过连接请求超时。同意,这不是解决方案
    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);
          });
        }
      }
    
    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);
        }
      }
    }