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