服務器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了。