面试:你懂什么是分布式系统吗?Redis分布式锁都不会?>>>
总览
本教程主要讨论Apache HttpClient 4框架的timeout设置。如果想学习HttpClient的其他方面,请参考HttpClient教程。
使用String参数配置Timeouts
HttpClient有许多参数配置,这些参数都可以使用一种通用的、类似map风格的方式进行设置。
以下是三个超时参数配置:
DefaultHttpClient httpClient = new DefaultHttpClient();
int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
httpParams.setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000);
httpParams.setParameter(
CoreConnectionPNames.SO_TIMEOUT, timeout * 1000);
// httpParams.setParameter(
// ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000));
尤其注意最后一个参数——连接管理器超时,在使用4.3.0 或者 4.3.1版本时应该被注释掉,具体请参考jira说明。
使用API配置Timeout
以下是通过类型安全的API来设置的方式:
DefaultHttpClient httpClient = new DefaultHttpClient();
int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(
httpParams, timeout * 1000); // http.connection.timeout
HttpConnectionParams.setSoTimeout(
httpParams, timeout * 1000); // http.socket.timeout
在HttpConnectionParams中没有提供设置第三个参数的setter方法,因此仍然需要我们手动通过调用setParameter方法来设置。
##使用 4.3 Builder API配置Timeout
4.3版本引入了基于fluent、builder操作的API,以下是设置方式:
int timeout = 5;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout * 1000)
.setConnectionRequestTimeout(timeout * 1000)
.setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client =
HttpClientBuilder.create().setDefaultRequestConfig(config).build();
这是一种基于类型安全和可读性来配置三个超时参数的推荐方式。
##超时属性解释
现在,我们来看一下这些不同类型超时参数的具体含义:
the Connection Timeout (http.connection.timeout) – 与远程服务器建立连接的时间
the Socket Timeout (http.socket.timeout) – 建立连接之后,等待远程服务器返回数据的时间,也就是两个数据包(请求包和响应包)之间不活动的最大时间。
the Connection Manager Timeout (http.connection-manager.timeout) – 从连接管理器/池中获取一个连接的等待时间。
前两个连接和socket超时的参数,是最重要的,但是获取一个连接的超时设置在高负载情况下也同样重要,这也就是第三个参数不能被忽略的原因所在。
使用 HttpClient
设置完上面的参数之后,HttpClient还不能被用来执行HTTP请求:
HttpGet getMethod = new HttpGet("http://host:8080/path");
HttpResponse response = httpClient.execute(getMethod);
System.out.println(
"HTTP Status of response: " + response.getStatusLine().getStatusCode());
根据前面的客户端设置,连接到主机的超时时间是5秒,如果建立连接但没有收到数据,超时还将额外增加5秒。
注意这2个异常:
- 连接超时返回的异常:org.apache.http.conn.ConnectTimeoutException
- socket超时返回的异常:java.net.SocketTimeoutExceptio
硬超时:Hard Timeout
虽然给HTTP连接建立和等待返回结果设置超时时间十分有用,但是有时我们也需要给整个请求设置一个硬超时时间。
例如,要下载一个可能很大的文件放到当前分类,在这种情况下,也许成功建立了连接,文件数据也许会不断传递给我们,但是我们也需要确保这个操作不会超过给定时间的阈值。
HttpClient没有任何配置,允许我们给一个请求设定一个总的超时时间。然而,可以通过HttpClient为请求提供终止功能,我们可以利用这个机制来实现一个简单的超时策略:
HttpGet getMethod = new HttpGet(
"http://localhost:8080/spring-security-rest-template/api/bars/1");
int hardTimeout = 5; // seconds
TimerTask task = new TimerTask() {
@Override
public void run() {
if (getMethod != null) {
getMethod.abort();
}
}
};
new Timer(true).schedule(task, hardTimeout * 1000);
HttpResponse response = httpClient.execute(getMethod);
System.out.println(
"HTTP Status of response: " + response.getStatusLine().getStatusCode());
我们利用java.util.Timer
和 java.util.TimerTask
来创建一个简单的延迟任务,实现在5秒硬超时之后,终止HTTP GET请求。
超时和DNS轮循需要注意的
一些大的域名使用DNS轮循调度配置是很常见的,本质上是一个域名映射到多个IP地址上。给这样的域名设置超时是一个新的挑战,仅仅是因为HttpClient将尝试连接到那个超时的域名:
- HttpClient 获取域名的IP列表
- 第一次尝试连接超时(由于我们的超时配置)
- 第二次尝试连接也超时
- 等等 …
因此,正如你所看到的,我们期望操作是不超时的。取而代之的是,当所有可能的路由超时的时候,整个操作就会超时。这对客户端来说是透明的(除非你配置了DEBUG级别的日志)。下面是一个简单的例子,您可以运行和重现这个问题:
int timeout = 3;
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://www.google.com:81");
response = client.execute(request);
在DEBUG日志中你将注意到以下重试逻辑:
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.212:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.212:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.208:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.208:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.209:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.209:81 timed out. Connection will be retried using another IP address
//...
结论
本教程讨论了如何给HttpClient配置各种不同的可用timeout参数,还举例说明了给一个不间断的HTTP连接建立一个简单的硬超时机制。
可以在GitHub上下载这些例子的代码实现,该项目是基于Maven实现的,因此导入和运行它很容易。
编译自:http://www.baeldung.com/httpclient-timeout