HttpClient是不错的http工具,可是每次升级api都变化很大,可能是4.x版本时间太短还不够稳定吧。今天在升级的时候遇到了一个问题。http请求设定的超时时间没有用了。之前获取httpclient实例的工具类如下
01
import
org.apache.http.conn.ClientConnectionManager;
02
import
org.apache.http.conn.params.ConnManagerParams;
03
import
org.apache.http.conn.params.ConnPerRouteBean;
04
import
org.apache.http.conn.scheme.PlainSocketFactory;
05
import
org.apache.http.conn.scheme.Scheme;
06
import
org.apache.http.conn.scheme.SchemeRegistry;
07
import
org.apache.http.conn.ssl.SSLSocketFactory;
08
import
org.apache.http.impl.client.DefaultHttpClient;
09
import
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
10
import
org.apache.http.params.BasicHttpParams;
11
import
org.apache.http.params.HttpConnectionParams;
12
import
org.apache.http.params.HttpParams;
13
14
public
class
HttpConnectionManager {
15
16
private
static
HttpParams httpParams;
17
private
static
ClientConnectionManager connectionManager;
18
19
/**
20
* 最大连接数
21
*/
22
public
final
static
int
MAX_TOTAL_CONNECTIONS =
800
;
23
/**
24
* 获取连接的最大等待时间
25
*/
26
public
final
static
int
WAIT_TIMEOUT =
60000
;
27
/**
28
* 每个路由最大连接数
29
*/
30
public
final
static
int
MAX_ROUTE_CONNECTIONS =
400
;
31
/**
32
* 连接超时时间
33
*/
34
public
final
static
int
CONNECT_TIMEOUT =
30000
;
35
/**
36
* 读取超时时间
37
*/
38
public
final
static
int
READ_TIMEOUT =
30000
;
39
40
static
{
41
httpParams =
new
BasicHttpParams();
42
// 设置最大连接数
43
44
ConnManagerParams.setMaxTotalConnections(httpParams,
45
MAX_TOTAL_CONNECTIONS);
46
// 设置获取连接的最大等待时间
47
ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);
48
// 设置每个路由最大连接数
49
ConnPerRouteBean connPerRoute =
new
ConnPerRouteBean(
50
MAX_ROUTE_CONNECTIONS);
51
ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);
52
// 设置连接超时时间
53
HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);
54
// 设置读取超时时间
55
HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);
56
57
SchemeRegistry registry =
new
SchemeRegistry();
58
registry.register(
new
Scheme(
"http"
, PlainSocketFactory
59
.getSocketFactory(),
80
));
60
registry.register(
new
Scheme(
"https"
, SSLSocketFactory
61
.getSocketFactory(),
443
));
62
63
connectionManager =
new
ThreadSafeClientConnManager(httpParams,
64
registry);
65
}
66
67
public
static
DefaultHttpClient getHttpClient() {
68
return
new
DefaultHttpClient(connectionManager, httpParams);
69
}
70
71
public
static
void
shutdown() {
72
connectionManager.shutdown();
73
}
74
75
}
很简单的一个类,初始化一些设置,然后提供一个getHttpClient方法,这里设定httpParam使用的是DefaultHttpClient的构造方法。很简单。
但是到了4.2~~~~。这个构造方法没有了。。xd,不再可以直接传递httpParams进去设定。
所以。。
1
HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);
2
HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);
根本就不管用。。。HttpConnectionParams的两个方法如下
01
public static void setConnectionTimeout(final HttpParams params, int timeout) {
02
if
(params == null) {
03
throw new IllegalArgumentException(
"HTTP parameters may not be null"
);
04
}
05
params.setIntParameter
06
(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
07
}
08
public static void setSoTimeout(final HttpParams params, int timeout) {
09
if
(params == null) {
10
throw new IllegalArgumentException(
"HTTP parameters may not be null"
);
11
}
12
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
13
14
}
就是给传进来的httpParams设定两个参数而已。那这两个set方法对应的get方法在哪里调用呢。。我们看代码。
观察 httpclient.execute(httpget)方法,跳到AbstractHttpClient的execute方法,代码如下
01
public
final
HttpResponse execute(HttpHost target, HttpRequest request,
02
HttpContext context)
03
throws
IOException, ClientProtocolException {
04
05
if
(request ==
null
) {
06
throw
new
IllegalArgumentException
07
(
"Request must not be null."
);
08
}
09
// a null target may be acceptable, this depends on the route planner
10
// a null context is acceptable, default context created below
11
12
HttpContext execContext =
null
;
13
RequestDirector director =
null
;
14
HttpRoutePlanner routePlanner =
null
;
15
ConnectionBackoffStrategy connectionBackoffStrategy =
null
;
16
BackoffManager backoffManager =
null
;
17
18
// Initialize the request execution context making copies of
19
// all shared objects that are potentially threading unsafe.
20
synchronized
(
this
) {
21
22
HttpContext defaultContext = createHttpContext();
23
if
(context ==
null
) {
24
execContext = defaultContext;
25
}
else
{
26
execContext =
new
DefaultedHttpContext(context, defaultContext);
27
}
28
// Create a director for this request
29
director = createClientRequestDirector(
30
getRequestExecutor(),
31
getConnectionManager(),
32
getConnectionReuseStrategy(),
33
getConnectionKeepAliveStrategy(),
34
getRoutePlanner(),
35
getProtocolProcessor(),
36
getHttpRequestRetryHandler(),
37
getRedirectStrategy(),
38
getTargetAuthenticationStrategy(),
39
getProxyAuthenticationStrategy(),
40
getUserTokenHandler(),
41
determineParams(request));
42
routePlanner = getRoutePlanner();
43
connectionBackoffStrategy = getConnectionBackoffStrategy();
44
backoffManager = getBackoffManager();
45
}
看41行的方法
01
protected
HttpParams determineParams(HttpRequest req) {
02
return
new
ClientParamsStack
03
(
null
, getParams(), req.getParams(),
null
);
04
}
05
//还有getParams()
06
public
synchronized
final
HttpParams getParams() {
07
if
(defaultParams ==
null
) {
08
defaultParams = createHttpParams();
09
}
10
return
defaultParams;
11
}
发现了吧。。httpclient的request参数,是这么初始化的,但是你根本就没给他传递过来httpParams,所以defaultParams是null,自己创建了一个。。我们看timeout的获取代码,在DefaultRequestDirector#execute 方法中,太长了,我就贴关键代码
1
long timeout = HttpClientParams.getConnectionManagerTimeout(params);
2
try {
3
managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS);
4
} catch(InterruptedException interrupted) {
5
InterruptedIOException iox = new InterruptedIOException();
6
iox.initCause(interrupted);
7
throw iox;
8
}
哦。原来之前在工具类中设定的timeout参数是这么获取的。看看getConnectionManagerTimeout干啥了。相关代码
1
public
static
int
getConnectionTimeout(
final
HttpParams params) {
2
if
(params ==
null
) {
3
throw
new
IllegalArgumentException(
"HTTP parameters may not be null"
);
4
}
5
return
params.getIntParameter
6
(CoreConnectionPNames.CONNECTION_TIMEOUT,
0
);
7
}
坑爹啊。传过来的params是刚才httpclient自己创建的。你去获得参数。当然是为空。。然后设定为0了。。所以timeout根本就不管用。。。
所以说。。在工具类里面设定HttpConnectionParams根本就不管用,因为你给httpParams设定的参数,httpclient根本就没接收到,最后的解决办法很简单,自己设定下httpParams好了。。修改后的代码
01
import
org.apache.http.conn.scheme.PlainSocketFactory;
02
import
org.apache.http.conn.scheme.Scheme;
03
import
org.apache.http.conn.scheme.SchemeRegistry;
04
import
org.apache.http.conn.ssl.SSLSocketFactory;
05
import
org.apache.http.impl.client.DefaultHttpClient;
06
import
org.apache.http.impl.conn.PoolingClientConnectionManager;
07
import
org.apache.http.params.BasicHttpParams;
08
import
org.apache.http.params.CoreConnectionPNames;
09
import
org.apache.http.params.CoreProtocolPNames;
10
import
org.apache.http.params.HttpParams;
11
12
public
class
HttpConnectionManager {
13
14
private
static
HttpParams httpParams;
15
private
static
PoolingClientConnectionManager cm;
16
17
/**
18
* 最大连接数
19
*/
20
public
final
static
int
MAX_TOTAL_CONNECTIONS =
200
;
21
/**
22
* 每个路由最大连接数
23
*/
24
public
final
static
int
MAX_ROUTE_CONNECTIONS =
20
;
25
/**
26
* 连接超时时间
27
*/
28
public
final
static
int
CONNECT_TIMEOUT =
30000
;
29
/**
30
* 读取超时时间
31
*/
32
public
final
static
int
READ_TIMEOUT =
30000
;
33
34
static
{
35
httpParams =
new
BasicHttpParams();
36
// 连接请求超时
37
httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
38
CONNECT_TIMEOUT);
39
// 数据读取超时
40
httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, READ_TIMEOUT);
41
// 编码
42
httpParams.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET,
43
"UTF-8"
);
44
SchemeRegistry schemeRegistry =
new
SchemeRegistry();
45
schemeRegistry.register(
new
Scheme(
"http"
,
80
, PlainSocketFactory
46
.getSocketFactory()));
47
schemeRegistry.register(
new
Scheme(
"https"
,
443
, SSLSocketFactory
48
.getSocketFactory()));
49
cm =
new
PoolingClientConnectionManager(schemeRegistry);
50
// Increase max total connection to 200
51
cm.setMaxTotal(MAX_TOTAL_CONNECTIONS);
52
// Increase default max connection per route to 20
53
cm.setDefaultMaxPerRoute(MAX_ROUTE_CONNECTIONS);
54
}
55
56
public
static
DefaultHttpClient getHttpClient() {
57
DefaultHttpClient httpClient =
new
DefaultHttpClient(cm);
58
httpClient.setParams(httpParams);
59
return
httpClient;
60
}
61
62
public
static
void
shutdown() {
63
cm.shutdown();
64
}
65
66
public
static
PoolingClientConnectionManager getConnectionManager() {
67
return
cm;
68
}
69
70
}
over了。。真是蛋疼啊。
ps:实际想想,既然没有了直接设定httpParams的地方,那应该提供方法了吧,还是我自己的思路有问题。。对httpclient了解不够深。。
参考:
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html
http://blog.youkuaiyun.com/shootyou/article/details/6415248
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html