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了解不够深。。

参考:

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html

http://blog.csdn.net/shootyou/article/details/6415248

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html

Comments
Write a Comment