<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
    主站蜘蛛池模板: 热99re久久免费视精品频软件 | 久久精品a亚洲国产v高清不卡| 精品国产麻豆免费网站| 91精品导航在线网址免费| 精品国产污污免费网站入口在线 | 麻豆国产精品免费视频| 光棍天堂免费手机观看在线观看| 无码亚洲成a人在线观看| 亚洲一级免费毛片| 亚洲v高清理论电影| 亚洲色偷偷综合亚洲AVYP| 亚洲精品麻豆av| 国产精品酒店视频免费看| 免费看国产精品3a黄的视频 | 亚洲视频2020| 亚洲爆乳无码一区二区三区| 亚洲国产人成中文幕一级二级| 日韩免费视频播播| 午夜视频免费成人| 成人a免费α片在线视频网站 | 亚洲一区二区三区国产精品无码| 亚洲欧洲一区二区| 亚洲电影一区二区| 久久久久久久久亚洲| 亚洲国产精品成人久久| 亚洲人成人网站色www| 亚洲欧洲日产国码av系列天堂| 亚洲精品和日本精品| 亚洲国产天堂久久综合| 亚洲不卡AV影片在线播放| 在线a亚洲v天堂网2018| 免费人成网站7777视频| 免费jlzzjlzz在线播放视频| 成人亚洲综合天堂| 亚洲片一区二区三区| 亚洲综合无码AV一区二区| 国产成人精品日本亚洲| 无码久久精品国产亚洲Av影片| 日韩精品一区二区亚洲AV观看| 亚洲精品午夜视频| 亚洲黄页网在线观看|