Axis2 webservice close_wait,java heap space錯誤的研究
2009-05-20 16:20
客戶端僞代碼如下
//主線程方法 while(true){ test.TestGetServiceStatus();//客戶端連接服務端的測試方法 try{ Thread.sleep(23 * 1000);//每隔23秒執行下webservice客戶端方法 }catch(Exception e){ e.printStackTrace(); } } private String wsUrl = "http://127.0.0.1:8080/axis2/services/webservice/"; public void TestGetServiceStatus(){ WebserviceStub stub = null; try{ stub = new WebserviceStub(wsUrl);//實例華客戶端 System.out.println("new a stub"); Thread.sleep(5000);//5秒鐘後執行客戶端向服務端的請求 String result = stub.queryServiceStatus(request).getResult();//請求服務端 System.out.println("result: " + result); //清除客戶端 stub._getServiceClient().cleanupTransport(); stub._getServiceClient().cleanup(); stub.cleanup(); stub = null; }catch(Exception e){ } } 步驟解釋: 1、主線程每隔23秒調用方法,來實現客戶端對服務端的調用 2、客戶端在對服務段進行藉口調用時: 2.1、實例化客戶端對象 2.2、睡5秒鐘 2.3、調用服務端接口,得到結果 2.4、清除客戶端 3、服務端的相應接口,收到請求後,睡眠25秒,給出結果。 4、webservice服務端配置文件裏,加入連接的存活時間,爲20s,如下(axis2.xml): <transportReceiver name="http" class="org.apache.axis2.transport.http.SimpleHTTPServer"> <parameter name="port">8080</parameter> <parameter name="threadKeepAliveTime">20</parameter> </transportReceiver> 基於以上的調用,用ethereal抓包得到下圖: (圖片看不清楚,可以點擊圖片進行查看) 注意兩個綠色框的內容,詳細過程分析如下: 1、new stub的時候,客戶端與服務端並沒有連接; 2、5秒鐘後,客戶端向服務端發送請求,連接建立,如圖的1—7個數據包; 3、測試客戶端做出處理,即睡眠25秒,之後返回數據,如圖的8—23個數據包; 4、客戶端收到數據後,cleanup,方法退出,但是此時看到連接並沒有關閉; 5、服務端在20秒後(設置的連接存活時間),斷開連接,如圖的24,25個數據包; 6、由於客戶端這邊在25秒的時候(23個數據包所示)退出了方法,過23秒後重新調用測試方法,於是應該在48秒的時候,重新new stub,這個時候,看圖知道,在第26個數據包的時候(也就是48秒的時候)客戶端發出了先前連接的斷開包,至此前一個連接完全釋放,開始下一輪的連接循環。 由此可以得出: 1、客戶端new stub的時候,並沒有與服務端建立連接,而只是釋放之前的連接; 2、連接是由服務端先發起的; 3、客戶端發送服務端請求的時候,連接由客戶端發起,並建立; 由上面的結論可以分析出一個問題:客戶端在調用服務端接口方法,並退出後,連接還存在,直到連接存活時間達到服務端設置的連接存活時間後,才被服務端所關閉,這個時候,如果客戶端不再重新發起請求,或者客戶端出錯(內存溢出,無法new stub等),那麼之前的那個連接就一直會處在close_wait狀態,而無法清除。這就是爲什麼有些webservice客戶端這邊,跑了一段時間後,會出現很多close_wait狀態的連接的緣故。 再來看,是什麼原因導致了new stub的時候出錯的呢?上面也講到了,雖然可能性有很多,但是有一個卻是大家經常犯的,那就是客戶端這邊內存溢出了。 很多webservice客戶端,在跑了很長時間後,發現內存就不夠用了,拋出java heap space的錯誤,隨之而來的就是類似Read Timeout阿等錯誤,這樣close_wait就越來越多,這些錯誤也會越拋越多……直到整個客戶端都無法與服務端取得任何連接,而進入“假死”狀態。那麼如何避免客戶端內存持續走高呢? 通過jdk自帶的jconsole和eclipse提供的memory analyzer可以看到,客戶端這邊的內存在不斷上升,java的gc根本無法釋放使用過的內存,而通過memory analyzer可以看到,很多堆內存都在客戶端調用服務端的方法裏,無法釋放(圖就不上了)。也就是說,這不是我們實現某功能時自己的原因導致的內存溢出,而是axis2本身的內存溢出。那是不是就沒有辦法解決了呢?不是的! 細心的你有沒有注意到開篇的時候我提供的客戶端的僞代碼?裏面有四句: stub._getServiceClient().cleanupTransport(); stub._getServiceClient().cleanup(); stub.cleanup(); stub = null; 當時加這四句代碼,只是爲了驗證客戶端時候能把連接主動關閉,可是結果是否定的,但是,恰好是這幾句解決了內存溢出的問題!只要在每次客戶端調用後,都執行以上四句代碼,那麼客戶端new出來的堆內存資源就可以被java的gc所回收掉了。 |