<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0
    http://blog.csdn.net/shootyou/article/details/6615051#comments


    今天解決了一個HttpClient的異常,汗啊,一個HttpClient使用稍有不慎都會是毀滅級別的啊。

    這里有之前因為route配置不當導致服務器異常的一個處理:http://blog.csdn.net/shootyou/article/details/6415248

    里面的HttpConnectionManager實現就是我在這里使用的實現。


    問題表現:

    tomcat后臺日志發現大量異常

    1. org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection  

    時間一長tomcat就無法繼續處理其他請求,從假死變成真死了。

    linux運行:

    1. netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'  
    發現CLOSE_WAIT的數量始終在400以上,一直沒降過。


    問題分析:

    一開始我對我的HttpClient使用過程深信不疑,我不認為異常是來自這里。

    所以我開始從TCP的連接狀態入手,猜測可能導致異常的原因。以前經常遇到TIME_WAIT數過大導致的服務器異常,很容易解決,修改下sysctl就ok了。但是這次是CLOSE_WAIT,是完全不同的概念了。

    關于TIME_WAIT和CLOSE_WAIT的區別和異常處理我會單獨起一篇文章詳細說說我的理解。


    簡單來說CLOSE_WAIT數目過大是由于被動關閉連接處理不當導致的。

    我 說一個場景,服務器A會去請求服務器B上面的apache獲取文件資源,正常情況下,如果請求成功,那么在抓取完資源后服務器A會主動發出關閉連接的請 求,這個時候就是主動關閉連接,連接狀態我們可以看到是TIME_WAIT。如果一旦發生異常呢?假設請求的資源服務器B上并不存在,那么這個時候就會由 服務器B發出關閉連接的請求,服務器A就是被動的關閉了連接,如果服務器A被動關閉連接之后自己并沒有釋放連接,那就會造成CLOSE_WAIT的狀態 了。

    所以很明顯,問題還是處在程序里頭。


    先看看我的HttpConnectionManager實現:

    1. public class HttpConnectionManager {   
    2.   
    3.     private static HttpParams httpParams;  
    4.     private static ClientConnectionManager connectionManager;  
    5.   
    6.     /** 
    7.      * 最大連接數 
    8.      */  
    9.     public final static int MAX_TOTAL_CONNECTIONS = 800;  
    10.     /** 
    11.      * 獲取連接的最大等待時間 
    12.      */  
    13.     public final static int WAIT_TIMEOUT = 60000;  
    14.     /** 
    15.      * 每個路由最大連接數 
    16.      */  
    17.     public final static int MAX_ROUTE_CONNECTIONS = 400;  
    18.     /** 
    19.      * 連接超時時間 
    20.      */  
    21.     public final static int CONNECT_TIMEOUT = 10000;  
    22.     /** 
    23.      * 讀取超時時間 
    24.      */  
    25.     public final static int READ_TIMEOUT = 10000;  
    26.   
    27.     static {  
    28.         httpParams = new BasicHttpParams();  
    29.         // 設置最大連接數  
    30.         ConnManagerParams.setMaxTotalConnections(httpParams, MAX_TOTAL_CONNECTIONS);  
    31.         // 設置獲取連接的最大等待時間  
    32.         ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);  
    33.         // 設置每個路由最大連接數  
    34.         ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);  
    35.         ConnManagerParams.setMaxConnectionsPerRoute(httpParams,connPerRoute);  
    36.         // 設置連接超時時間  
    37.         HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);  
    38.         // 設置讀取超時時間  
    39.         HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);  
    40.   
    41.         SchemeRegistry registry = new SchemeRegistry();  
    42.         registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));  
    43.         registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));  
    44.   
    45.         connectionManager = new ThreadSafeClientConnManager(httpParams, registry);  
    46.     }  
    47.   
    48.     public static HttpClient getHttpClient() {  
    49.         return new DefaultHttpClient(connectionManager, httpParams);  
    50.     }  
    51.   
    52. }  


    看到沒MAX_ROUTE_CONNECTIONS 正好是400,跟CLOSE_WAIT非常接近啊,難道是巧合?繼續往下看。

    然后看看調用它的代碼是什么樣的:
    1. public static String readNet (String urlPath)  
    2.     {  
    3.         StringBuffer sb = new StringBuffer ();  
    4.         HttpClient client = null;  
    5.         InputStream in = null;  
    6.         InputStreamReader isr = null;  
    7.         try  
    8.         {  
    9.             client = HttpConnectionManager.getHttpClient();  
    10.             HttpGet get = new HttpGet();  
    11.             get.setURI(new URI(urlPath));  
    12.             HttpResponse response = client.execute(get);  
    13.             if (response.getStatusLine ().getStatusCode () != 200) {  
    14.                 return null;  
    15.             }  
    16.             HttpEntity entity =response.getEntity();  
    17.               
    18.             if( entity != null ){  
    19.                 in = entity.getContent();  
    20.                 .....  
    21.             }  
    22.             return sb.toString ();  
    23.               
    24.         }  
    25.         catch (Exception e)  
    26.         {  
    27.             e.printStackTrace ();  
    28.             return null;  
    29.         }  
    30.         finally  
    31.         {  
    32.             if (isr != null){  
    33.                 try  
    34.                 {  
    35.                     isr.close ();  
    36.                 }  
    37.                 catch (IOException e)  
    38.                 {  
    39.                     e.printStackTrace ();  
    40.                 }  
    41.             }  
    42.             if (in != null){  
    43.                 try  
    44.                 {  
    45.                     <span style="color:#ff0000;">in.close ();</span>  
    46.                 }  
    47.                 catch (IOException e)  
    48.                 {  
    49.                     e.printStackTrace ();  
    50.                 }  
    51.             }  
    52.         }  
    53.     }  

    很簡單,就是個遠程讀取中文頁面的方法。值得注意的是這一段代碼是后來某某同學加上去的,看上去沒啥問題,是用于非200狀態的異常處理:
    1. if (response.getStatusLine ().getStatusCode () != 200) {  
    2.                 return null;  
    3.             }  

    代碼本身沒有問題,但是問題是放錯了位置。如果這么寫的話就沒問題:
    1. client = HttpConnectionManager.getHttpClient();  
    2.             HttpGet get = new HttpGet();  
    3.             get.setURI(new URI(urlPath));  
    4.             HttpResponse response = client.execute(get);  
    5.               
    6.             HttpEntity entity =response.getEntity();  
    7.               
    8.             if( entity != null ){  
    9.                 in = entity.getContent();  
    10.             ..........  
    11.             }  
    12.               
    13.             if (response.getStatusLine ().getStatusCode () != 200) {  
    14.                 return null;  
    15.             }  
    16.             return sb.toString ();  
    看出毛病了吧。在這篇入門(HttpClient4.X 升級 入門 + http連接池使用) 里頭我提到了HttpClient4使用我們常用的InputStream.close()來確認連接關閉,前面那種寫法InputStream in 根本就不會被賦值,意味著一旦出現非200的連接,這個連接將永遠僵死在連接池里頭,太恐怖了。。。所以我們看到CLOST_WAIT數目為400,因為 對一個路由的連接已經完全被僵死連接占滿了。。。

    其實上面那段代碼還有一個沒處理好的地方,異常處理不夠嚴謹,所以最后我把代碼改成了這樣:

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

    顯示調用HttpGet的abort,這樣就會直接中止這次連接,我們在遇到異常的時候應該顯示調用,因為誰能保證異常是在InputStream in賦值之后才拋出的呢。


    好了 ,分析完畢,明天準備總結下CLOSE_WAIT和TIME_WAIT的區別。

    posted on 2012-07-16 12:02 SIMONE 閱讀(5154) 評論(0)  編輯  收藏 所屬分類: JAVA
    主站蜘蛛池模板: 亚洲成人在线电影| 亚洲熟妇无码一区二区三区导航| 成av免费大片黄在线观看| 国产亚洲精品精华液| 99久久久国产精品免费无卡顿| 亚洲国产成人精品无码区二本| 中国亚洲女人69内射少妇| 亚洲中文无码永久免费| 国产99视频精品免费视频76| 亚洲国产精品专区| 亚洲无线一二三四区手机| 亚洲黄色免费网站| a级毛片免费网站| 中文字幕亚洲综合小综合在线| 中文字幕亚洲日韩无线码| 91在线视频免费91| 成人性生交大片免费看好| 亚洲成a人片在线不卡一二三区| 日韩精品亚洲aⅴ在线影院| 成年女人喷潮毛片免费播放| 中国一级毛片免费看视频| 亚洲欧美日韩综合俺去了| 亚洲AV色香蕉一区二区| 免费在线观看理论片| 四虎在线成人免费网站| 91视频免费网站| 国产午夜亚洲精品不卡| 亚洲伊人久久大香线蕉在观| 亚洲精品高清国产一线久久| 最好免费观看韩国+日本| 96免费精品视频在线观看| 国产在线播放线91免费| 爱情岛亚洲论坛在线观看| 色在线亚洲视频www| 亚洲国产精品婷婷久久| 亚洲一区AV无码少妇电影☆| 免费真实播放国产乱子伦| 无码一区二区三区免费视频| 最近中文字幕大全免费视频 | 日韩在线观看免费| 亚洲综合小说另类图片动图|