HttpClient 与 Close_Wait

服务器A须要经过HttpClient去链接另外一个系统B提供的服务,运行一段时间后抛出如下异 常:java.net.SocketException: Connection reset by peer: socket write error close_wait java

在服务器B上运行netstat命令,发现大量链接处于CLOSE_WAIT 状态。 apache

问题分析: 服务器

简单来讲CLOSE_WAIT数目过大是因为被动关闭链接处理不当致使的。 网络

我说一个场景,服务器A会去请求服务器B上面的apache获取文件资源,正常状况下,若是请求成功,那么在抓取完资源后服务器A会主动发出关闭连 接的请求,这个时候就是主动关闭链接,链接状态咱们能够看到是TIME_WAIT。若是一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候 就会由服务器B发出关闭链接的请求,服务器A就是被动的关闭了链接,若是服务器A被动关闭链接以后本身并无释放链接,那就会形成CLOSE_WAIT的 状态了。 socket

因此很明显,问题仍是处在程序里头。 tcp

原始代码块: url

 
  1. try 
  2. client = HttpConnectionManager.getHttpClient(); 
  3. HttpGet get = new HttpGet(); 
  4. get.setURI(new URI(urlPath)); 
  5. HttpResponse response = client.execute(get); 
  6. if (response.getStatusLine ().getStatusCode () != 200) { 
  7. return null; 
  8. HttpEntity entity =response.getEntity(); 
  9.  
  10. if( entity != null ){ 
  11. in = entity.getContent(); 
  12. ..... 
  13. return sb.toString (); 
  14.  
  15. catch (Exception e) 
  16. e.printStackTrace (); 
  17. return null; 
  18. finally 
  19. if (isr != null){ 
  20. try 
  21. isr.close (); 
  22. catch (IOException e) 
  23. e.printStackTrace (); 
  24. if (in != null){ 
  25. try 
  26. <span style="color:#ff0000;">in.close ();</span> 
  27. catch (IOException e) 
  28. e.printStackTrace (); 

HttpClient使用咱们经常使用的InputStream.close()来确认链接关闭,分析上面的代码,一旦出现非200的链接,这个链接将永远僵死在链接池里头,由于inputStream得不到初始化,永远不会调用close()方法了。 spa

经过代码稍微修改,更严谨的处理异常状况就能够解决问题了: .net

 
  1. public static String readNet (String urlPath) 
  2. StringBuffer sb = new StringBuffer (); 
  3. HttpClient client = null
  4. InputStream in = null
  5. InputStreamReader isr = null
  6. HttpGet get = new HttpGet(); 
  7. try 
  8. client = HttpConnectionManager.getHttpClient(); 
  9. get.setURI(new URI(urlPath)); 
  10. HttpResponse response = client.execute(get); 
  11. if (response.getStatusLine ().getStatusCode () != 200) { 
  12. get.abort(); 
  13. return null; 
  14. HttpEntity entity =response.getEntity(); 
  15.  
  16. if( entity != null ){ 
  17. in = entity.getContent(); 
  18. ...... 
  19. return sb.toString (); 
  20.  
  21. catch (Exception e) 
  22. get.abort(); 
  23. e.printStackTrace (); 
  24. return null; 
  25. finally 
  26. if (isr != null){ 
  27. try 
  28. isr.close (); 
  29. catch (IOException e) 
  30. e.printStackTrace (); 
  31. if (in != null){ 
  32. try 
  33. in.close (); 
  34. catch (IOException e) 
  35. e.printStackTrace (); 

显示调用HttpGet的abort,这样就会直接停止此次链接,咱们在遇到异常的时候应该显示调用,由于谁能保证异常是在InputStream in赋值以后才抛出的呢。 code

more:

首先咱们知道,若是咱们的服务器程序处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!

由于若是是CLIENT端主动断掉当前链接的话,那么双方关闭这个TCP链接共须要四个packet:

Client –-> FIN  –-> Server
Client <–- ACK  <–- Server
这时候Client端处于FIN_WAIT_2状态;而Server 程序处于CLOSE_WAIT状态。
Client <–- FIN  <–- Server
这时Server 发送FIN给Client,Server 就置为LAST_ACK状态。
Client –-> ACK  –-> Server
Client回应了ACK,那么Server 的套接字才会真正置为CLOSED状态。

Server 程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明尚未发FIN给Client,那么多是在关闭链接以前还有许多数据要发送或者其余事要作,致使没有发这个FIN packet。

一般来讲,一个CLOSE_WAIT会维持至少2个小时的时间(这个时间外网服务器一般会作调整,要否则太危险了)。若是有个流氓特意写了个程序,给你形成一堆的CLOSE_WAIT,消耗

你的资源,那么一般是等不到释放那一刻,系统就已经解决崩溃了。

只能经过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个问题。

可是实际上,仍是主要是由于咱们的程序代码有问题,

more:

最近作httpclient作转发服务,发现服务器上老是有不少close_wait状态的链接,并且这些链接都不会关闭,最后致使服务器无法创建新的网络链接,从而中止响应。

后来在网上搜索了一下,发现解决的方法也很简单,若是想重用链接,那就使用链接管理器,从链接管理器里获取链接,而后定时的用链接管理器来释放空闲链接。httpclient自带了SimpleHttpConnectionManager,提供了

Java代码 

 
  1. closeIdleConnections(long idleTimeout)  

这样的方法。

若是不须要重用连接,则直接在httpmethod建立时,设置一个http头信息就能够了

Java代码

 
  1. httpmethod.setRequestHeader("Connection", "close");   

这样就不会有恼人的close_wait了。

相关文章
相关标签/搜索