[size=medium]在测试环境发现了一个问题,每次服务器重启后,所有对新浪微博的请求都可以正常的发出,但从第9请求开始,请求耗时将变为40s左右。
问题要从9和40s这两个关键词开始,httpclient的代码如下:
[/size]
[size=medium]可以看到我们设置了setDefaultMaxConnectionsPerHost为8,setLinger为40000,经过查询资料推测原因如下:
首先我们先要澄清几个概念
1、对于tcp请求来说,tcp的客户端服务端概念和http的不同,请求双方,哪边关闭请求,哪边就是tcp客户端,另一边就为服务端。
2、tcp的一个链接由4个值确定,源ip、源端口、目标ip、目标地址。
tcp状态图中有一个TIME_WAIT状态,也叫2MSL状态,该状态是为了保证在tcp客户端发给tcp服务端最后一个ACK能顺利到达的一个等待状态。若没有TIME_WAIT状态,tcp客户端将直接进入CLOSED 状态,但有时由于网络原因,ACK 响应丢失,tcp服务端收不到 ACK,将重发FIN,CLOSED 状态的tcp客户端将返回RST ,而不是ACK。将导致tcp服务器异常。
也就是为了这个原因,tcp的客户端将会把连接保留在2MSL状态一段较长的时间。在这段时间里等待重发的FIN。这里需要注意的是,在2MSL状态的连接是不可以被重新使用。
我们上面的代码设置了对单个站点只能有最大的连接8个,对于httpclient的应用,这8个连接只有8个端口,同一个ip。若在2MSL的时间里,8个请求都被耗尽,那么第9个请求就必须等待2MSL状态结束。
而新浪服务器估计是由于轮询的原因,会尽可能的保持着连接,所以接收到所有数据的httpclient应用,会发起关闭连接,此时就进入了2MSL的状态。
解决方案也很简单,增加连接数 setDefaultMaxConnectionsPerHost ,或减少2MSL状态(TIME_WAIT)setLinger 的时间。
[/size]
问题要从9和40s这两个关键词开始,httpclient的代码如下:
[/size]
ProtocolSocketFactory protocolSocketFactory = new EasySSLProtocolSocketFactory();
Protocol.registerProtocol("https", new Protocol("https", protocolSocketFactory, 443));
client.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost(8);
client.getHttpConnectionManager().getParams().setMaxTotalConnections(100);
client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
client.getHttpConnectionManager().getParams().setSoTimeout(5000);
client.getHttpConnectionManager().getParams().setTcpNoDelay(true);
client.getHttpConnectionManager().getParams().setLinger(40000);
client.getHttpConnectionManager().getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
client.getHttpConnectionManager().getParams().setStaleCheckingEnabled(true);
[size=medium]可以看到我们设置了setDefaultMaxConnectionsPerHost为8,setLinger为40000,经过查询资料推测原因如下:
首先我们先要澄清几个概念
1、对于tcp请求来说,tcp的客户端服务端概念和http的不同,请求双方,哪边关闭请求,哪边就是tcp客户端,另一边就为服务端。
2、tcp的一个链接由4个值确定,源ip、源端口、目标ip、目标地址。
tcp状态图中有一个TIME_WAIT状态,也叫2MSL状态,该状态是为了保证在tcp客户端发给tcp服务端最后一个ACK能顺利到达的一个等待状态。若没有TIME_WAIT状态,tcp客户端将直接进入CLOSED 状态,但有时由于网络原因,ACK 响应丢失,tcp服务端收不到 ACK,将重发FIN,CLOSED 状态的tcp客户端将返回RST ,而不是ACK。将导致tcp服务器异常。
也就是为了这个原因,tcp的客户端将会把连接保留在2MSL状态一段较长的时间。在这段时间里等待重发的FIN。这里需要注意的是,在2MSL状态的连接是不可以被重新使用。
我们上面的代码设置了对单个站点只能有最大的连接8个,对于httpclient的应用,这8个连接只有8个端口,同一个ip。若在2MSL的时间里,8个请求都被耗尽,那么第9个请求就必须等待2MSL状态结束。
而新浪服务器估计是由于轮询的原因,会尽可能的保持着连接,所以接收到所有数据的httpclient应用,会发起关闭连接,此时就进入了2MSL的状态。
解决方案也很简单,增加连接数 setDefaultMaxConnectionsPerHost ,或减少2MSL状态(TIME_WAIT)setLinger 的时间。
[/size]