服务器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
- try
- {
- client = HttpConnectionManager.getHttpClient();
- HttpGet get = new HttpGet();
- get.setURI(new URI(urlPath));
- HttpResponse response = client.execute(get);
- if (response.getStatusLine ().getStatusCode () != 200) {
- return null;
- }
- HttpEntity entity =response.getEntity();
- if( entity != null ){
- in = entity.getContent();
- .....
- }
- return sb.toString ();
- }
- catch (Exception e)
- {
- e.printStackTrace ();
- return null;
- }
- finally
- {
- if (isr != null){
- try
- {
- isr.close ();
- }
- catch (IOException e)
- {
- e.printStackTrace ();
- }
- }
- if (in != null){
- try
- {
- <span style="color:#ff0000;">in.close ();</span>
- }
- catch (IOException e)
- {
- e.printStackTrace ();
- }
- }
- }
HttpClient使用咱们经常使用的InputStream.close()来确认链接关闭,分析上面的代码,一旦出现非200的链接,这个链接将永远僵死在链接池里头,由于inputStream得不到初始化,永远不会调用close()方法了。 spa
经过代码稍微修改,更严谨的处理异常状况就能够解决问题了: .net
- public static String readNet (String urlPath)
- {
- StringBuffer sb = new StringBuffer ();
- HttpClient client = null;
- InputStream in = null;
- InputStreamReader isr = null;
- HttpGet get = new HttpGet();
- try
- {
- client = HttpConnectionManager.getHttpClient();
- get.setURI(new URI(urlPath));
- HttpResponse response = client.execute(get);
- if (response.getStatusLine ().getStatusCode () != 200) {
- get.abort();
- return null;
- }
- HttpEntity entity =response.getEntity();
- if( entity != null ){
- in = entity.getContent();
- ......
- }
- return sb.toString ();
- }
- catch (Exception e)
- {
- get.abort();
- e.printStackTrace ();
- return null;
- }
- finally
- {
- if (isr != null){
- try
- {
- isr.close ();
- }
- catch (IOException e)
- {
- e.printStackTrace ();
- }
- }
- if (in != null){
- try
- {
- in.close ();
- }
- catch (IOException e)
- {
- 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代码
- closeIdleConnections(long idleTimeout)
这样的方法。
若是不须要重用连接,则直接在httpmethod建立时,设置一个http头信息就能够了
Java代码
- httpmethod.setRequestHeader("Connection", "close");
这样就不会有恼人的close_wait了。