Httpclient超時
背景:
網站這邊多次因爲httpclient調用超時時間沒設置好導致關掉,影響非常不好,而且問題重複出現,查看網絡,沒有比較明確介紹httpclient所有超時相關的設置(大部分只提到連接超時(connectintimeout),讀超時(sockettimeout),對連接池超時提到的比較少),因此本文對超時這塊最介紹,其他功能性內容,apache官方提供了比較詳細的解析,這裏不做討論。具體可見:http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html
網站這邊用的是:
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
配置:
privatefinalstaticMultiThreadedHttpConnectionManagermanager=newMultiThreadedHttpConnectionManager();
privatefinalstaticHttpClienthttpclient=newHttpClient(manager);
httpclient.setConnectionTimeout(1000);
httpclient.setTimeout(1000);
現象:
頁面無法打開,堆棧信息如下:
Name: trhead-142
State: WAITING onorg.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool@69a4cb
Total blocked: 0 Total waited: 1
Stack trace:
java.lang.Object.wait(Native Method)
org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.doGetConnection(MultiThreadedHttpConnectionManager.java:518)
org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.getConnectionWithTimeout(MultiThreadedHttpConnectionManager.java:416)
org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:153)
org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
com.madding.test.MyRunnable.run(MyTest1.java:53)
java.lang.Thread.run(Thread.java:619)
大部分線程等待在:org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.doGetConnection
分析:
問題:很明顯連接池超時沒設置,導致請求在線程池中等待,進而引起dubb無法處理其他請求。
解決:加httpclient.setHttpConnectionFactoryTimeout(1000);,設置連接池超時。
說明:廢棄方法儘量不要用
進一步分析:
爲什麼沒設置這個會導致請求等待:
MultiThreadedHttpConnectionManager代碼在獲取連接時去連接池取,而連接池在沒設置超時timeToWait爲0,即一直處於等待狀態,如果沒有notify不會結束等待。
試用
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2</version>
</dependency>
發現代碼中即使不設置線程池超時,在高併發下也能正常訪問,查看代碼,發現是因爲在默認沒設置線程池超時時把連接超時的時間作爲線程超時時間,代碼如下:
而且httpclient4的代碼結構相對3做了很大重構,且實現比較優雅,可以考慮在本地私服把4.x版本添加進來。
完整的超時可參考如下:
staticPoolingClientConnectionManagerconnectionManager=null;
staticHttpClienthttpclient =null;
static{
connectionManager=newPoolingClientConnectionManager();
connectionManager.setMaxTotal(1);
httpclient=newDefaultHttpClient(connectionManager);
httpclient.getParams().setParameter("http.socket.timeout",1000);
httpclient.getParams().setParameter("http.connection.timeout",1000);
httpclient.getParams().setParameter("http.connection-manager.timeout",100000000L);
}
具體測試代碼如下:
3.1:
packagecom.madding.test;
importorg.apache.commons.httpclient.DefaultMethodRetryHandler;
importorg.apache.commons.httpclient.HttpClient;
importorg.apache.commons.httpclient.HttpStatus;
importorg.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
importorg.apache.commons.httpclient.methods.GetMethod;
/**
*@author madding.lip
*/
publicclassMyHttpClientTest3_1 {
privatefinalstaticMultiThreadedHttpConnectionManagermanager=newMultiThreadedHttpConnectionManager();
privatefinalstaticHttpClienthttpclient=newHttpClient(manager);
publicstaticvoidmain(String[] args) {
httpclient.getParams().setParameter("http.socket.timeout",1000);
httpclient.getParams().setParameter("http.connection.timeout",1000);
httpclient.getParams().setParameter("http.connection-manager.timeout",60*60L);
// httpclient.setConnectionTimeout(1000);
// httpclient.setTimeout(1000);
// httpclient.setHttpConnectionFactoryTimeout(1000);
for(inti = 0; true;i++) {
newThread(newMyRunnable(httpclient),"trhead-"+ i).start();
try{
Thread.sleep(100);
}catch(InterruptedException e) {
}
}
}
}
classMyRunnableimplementsRunnable {
HttpClienthttpclient=null;
MyRunnable(HttpClient client){
httpclient= client;
}
publicvoidrun() {
GetMethod getMethod =newGetMethod("http://www.apache.org/");
try{
DefaultMethodRetryHandlerretryHandler
= newDefaultMethodRetryHandler();
retryHandler.setRetryCount(0);
getMethod.setMethodRetryHandler(retryHandler);
intstatusCode =httpclient.executeMethod(getMethod);
if(statusCode == HttpStatus.SC_OK){
getMethod.getResponseBodyAsString();
}
}catch(Exception e) {
System.err.println(e);
}finally{
getMethod.releaseConnection();
}
}
}
4.2:
packagecom.madding.test;
importjava.io.IOException;
importorg.apache.http.HttpEntity;
importorg.apache.http.HttpResponse;
importorg.apache.http.client.ClientProtocolException;
importorg.apache.http.client.HttpClient;
importorg.apache.http.client.methods.HttpGet;
importorg.apache.http.client.methods.HttpUriRequest;
importorg.apache.http.impl.client.DefaultHttpClient;
importorg.apache.http.impl.conn.PoolingClientConnectionManager;
importorg.apache.http.util.EntityUtils;
/**
*@author madding.lip
*/
publicclassMyHttpClientTest4_2 {
staticPoolingClientConnectionManagerconnectionManager=null;
staticHttpClienthttpclient =null;
static{
connectionManager=newPoolingClientConnectionManager();
connectionManager.setMaxTotal(1);
httpclient=newDefaultHttpClient(connectionManager);
httpclient.getParams().setParameter("http.socket.timeout",1000);
httpclient.getParams().setParameter("http.connection.timeout",1000);
httpclient.getParams().setParameter("http.connection-manager.timeout",100000000L);
}
publicstaticvoidmain(String[] args) {
for(inti = 0; true;i++) {
newThread(newMyTest(httpclient),"trhead-"+ i).start();
try{
Thread.sleep(100);
}catch(InterruptedException e) {
}
}
}
}
classMyTestimplementsRunnable {
staticHttpClienthttpclient =null;
publicMyTest(HttpClient hc){
httpclient= hc;
}
publicvoidrun() {
HttpUriRequest httpget =newHttpGet("http://www.apache.org/");
try{
HttpResponse response =httpclient.execute(httpget);
HttpEntity entity =response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if(entity !=null){
System.out.println("Responsecontent length: " +entity.getContentLength());
EntityUtils.toString(entity);
//System.out.println(EntityUtils.toString(entity));
}
System.out.println("----------------------------------------");
}catch(ClientProtocolException e) {
System.err.println(e);
}catch(IOException e) {
System.err.println(e);
}finally{
if(httpget !=null){
httpget.abort();
}
}
}
}