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