最新消息:欢迎各位技术大牛一起交流讨论,邮箱:gww0426@163.com

HttpClient优化

Java技术 郭伟伟 50510浏览 0评论

最近的一个项目中需要用HttpClient请求多台远程服务器。由于接口调用有点频繁,结果出现了很多ConnectTimeoutException,于是对程序进行了优化。

业务逻辑的优化和减少请求次数的优化就不说了,下面重点说说HttpClient的优化。

先看看以前的代码,每次请求都会创建一个HttpClient,代码如下:

HttpClient client = getHttpClient(5000, 5000);

GetMethod getMethod = new GetMethod(url);

getMethod.setQueryString(parameter);

byte[] responseBody = null;

try {

    int statusCode = client.executeMethod(getMethod);

    if (statusCode != HttpStatus.SC_OK) {

        logger.error("error !" + url + ",statusCode " + statusCode);

    }

    responseBody = getMethod.getResponseBody();

}catch (IOException e) {

    logger.error("error!" + url, e);

} finally {

   getMethod.releaseConnection();

}

这是最简单的代码,很多例子就是这样开始的,如果少量请求还行,请求很频繁的话,这样的代码不是很好。(注意这里连接用完后必须释放连接)。

首先,HttpClient可以共用,减少创建HttpClient的开销。当然,如果你的应用调用HttpClient并不怎么频繁的话那就没必要共用了,毕竟在内存中维护一个空闲的httpClient对象是不保险的。

其次,Connection可以重用,减少建立连接的开销。

要完成以上两点,可以用多线程下的MultiThreadedHttpConnectionManager管理HttpConnection和HttpClient。MultiThreadedHttpConnectionManager管理的HttpClient是线程安全的,可以做成单例的。但是值得注意的是每个线程应该有自己的HttpMethod和HttpState、HttpConfiguration,以区分每次请求的host和HttpSession 。如下所示:

private static MultiThreadedHttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager();

private HttpClient client = new HttpClient(httpConnectionManager);

定义好后,在静态块中初始化相关参数

static {              

    //每主机最大连接数和总共最大连接数,通过hosfConfiguration设置host来区分每个主机  

    client.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost(8);

    client.getHttpConnectionManager().getParams().setMaxTotalConnections(48);

    client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);

    client.getHttpConnectionManager().getParams().setSoTimeout(5000);

    client.getHttpConnectionManager().getParams().setTcpNoDelay(true);

    client.getHttpConnectionManager().getParams().setLinger(1);                   

    //失败的情况下会进行3次尝试,成功之后不会再尝试

    client.getHttpConnectionManager().getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());

}

HttpConfiguration中有一些参数对提高性能有一些帮助,主要说明如下:

DefaultMaxConnectionsPerHost参数定义每台主机允许的最大连接数,默认为2。这个参数只能用于一些特定的httpConnectionManager,比如MultiThreadedHttpConnectionManager。

MaxTotalConnections参数表示httpConnectionManager管理的最大连接数,默认为20。同上个参数,这个参数也只是在某些特定的httpConnectionManager中有用。

setTcpNoDelay(true)设置是否启用Nagle算法,设置true后禁用Nagle算法,默认为false(即默认启用Nagle算法)。Nagle算法试图通过减少分片的数量来节省带宽。当应用程序希望降低网络延迟并提高性能时,它们可以关闭Nagle算法,这样数据将会更早地发送,但是增加了网络消耗。

setLinger(1)设置socket延迟关闭时间,单位为s,值为0表示这个选项是关闭的,值为-1表示使用JRE的默认设置。

setStaleCheckingEnabled(true)参数设置是否启用旧连接检查,默认是开启的。关闭这个旧连接检查可以提高一点点性能,但是增加了I/O错误的风险(当服务端关闭连接时)。开启这个选项则在每次使用老的连接之前都会检查连接是否可用,这个耗时大概在15-30ms之间[3]

然后,在每个线程代码中,创建自己的HttpMethod。

还有一个很重要的优化点是采用请求/响应实体流,尤其是在请求频繁数据量大的情况下,很大的实体不会被缓存在内存中而直接发送或接收,采用流能有效的提高性能。虽然这些实体可以是字符串或者字节数组,但是它们容易导致内存泄露,你得小心的使用他们,因为它们是整个实体缓存在内存中的。

对于响应流,采用method的getResponseBodyAsStream()来代替getResponseBody() 和getResponseBodyAsString().如下所示:

HttpClient httpclient = new HttpClient();

  GetMethod httpget = new GetMethod("http://www.myhost.com/");

  try {

    httpclient.executeMethod(httpget);

    Reader reader = new InputStreamReader(httpget.getResponseBodyAsStream(), httpget.getResponseCharSet());

    // consume the response entity

  } finally {

    httpget.releaseConnection();

  }

而对于请求流,可以通过实现RequestEntity自定义自己的各种流,httpclient包含了常见的几种实现,如FileRequestEntity、ByteArrayRequestEntity、StringRequestEntity、MultipartRequestEntity等。使用示例如下:

File myfile = new File("myfile.txt");

PostMethod httppost = new PostMethod("/stuff");

httppost.setRequestEntity(new FileRequestEntity(myfile));

如果客户端和服务器的通讯不需要保持会话状态的话,可以通过禁用Cookie来提高一点点性能,比如蜘蛛爬虫之类应用。如下:

HttpMethod method = new GetMethod();

method.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);

method.setRequestHeader("Cookie", "special-cookie=value");

参考资料:

1. http://hc.apache.org/httpclient-3.x/performance.html

转载请注明:郭伟伟@互联网 » HttpClient优化


发表我的评论
取消评论

使用新浪微博登陆

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (4)

  1. 这个坑也踩过
    nickolas2014-05-20 17:19 回复
  2. You can certainly see your skills within the work you write. The sector hopes for even more passionate writers such as you who aren't afraid to say how they believe. Always follow your heart.
    Laticia Woodside2014-04-28 16:20 回复
  3. 纠正一个错误,setLinger(1)中参数的单位为秒(s),之前一直理解的是ms 今天看到毕玄发的“API单位误解造成的严重故障”才发现这个问题,还不知道是不是自己以前写的那段代码呢,惭愧。
    郭伟伟2013-12-12 13:35 回复
  4. 很强大!
    Willie2012-03-17 23:47 回复