HttpClient超时机制

转自:http://agapple.iteye.com/blog/916837

HttpClient版本是3.0.1

一般的HttpClient使用例子:

这里我给出的一个url是近20MB的一个下载资源,很快发现线程要等个很久。 咋办,得加个timeout超时机制。
Stack线程栈代码

分析

目前httpClient3.1只支持3种timeout的设置:

  • connectionTimeout  :  socket建立链接的超时时间,Httpclient包中通过一个异步线程去创建socket链接,对应的超时控制。
  • timeoutInMilliseconds :  socket read数据的超时时间, socket.setSoTimeout(timeout);
  • httpConnectionTimeout :  如果那个的是MultiThreadedHttpConnectionManager,对应的是从连接池获取链接的超时时间。

分析一下问题,我们需要的是一个HttpClient整个链接读取的一个超时时间,包括请求发起,Http Head解析,response流读取的一系列时间的总和。

目标很明确,对应的修正后的测试代码:

这里通过Thread.join方法,设置了超时时间为5000 ms,这是比较早的用法。 如果熟悉cocurrent包的,可以直接使用Future和ThreadPoolExecutor进行异步处理,缓存对应的Thread。

Cocurrent代码例子代码:

说明: 这里为什么释放链接未采用get.releaseConnection()
看下release的实现:

  • 这里会先关闭responseStream流,这就是问题点。
  • 对应的responseStream是在方法:readResponseBody(HttpConnection conn)。一般的html页面返回的是一个ContentLengthInputStream对象
  • ContentLengthInputStream在调用close方法时会用ChunkedInputStream.exhaustInputStream读完所有流数据
  • ChunkedInputStream.exhaustInputStream代码

说明:

  • 因为非sleep和park的方法,不会响应InterruptedException事件,所以普通future超时发起的Thread.interrpt()并没有效果。
  • 默认的SimpleHttpConnectionManager不支持这样的操作,所以选择MultiThreadedHttpConnectionManager.shutdown()方法,强制关闭底层HttpConnection的sock的输入输出流。

总结

  1. 理解一下HttpClient这样设计的理由: socket重用,keepAlive协议的支持等,保证上一次数据不会对新的请求有影响。
  2. Thread.interrpt()处理,只会在Thread处于sleep或者wait状态才会被唤醒(api的描述)。而且该方法的调用并不自动产生InterruptedException异常,一般是需要自己判断Thread.isInterrupted(),然后throw异常。 我们目前使用的一些jdk cocurrent类比如future.cancel也是类似处理。