Apache commons 系列的HttpClient 相信你們都用過,選擇它而非JDK 的java.net.HttpURLConnection ,是爲了使用HttpClient 封裝的幾個實用的功能。html
目前使用最多的版本仍是httpclient-3.x ,在官網http://hc.apache.org/httpclient-3.x/tutorial.html 有這麼一段示例代碼:java
import org.apache.commons.httpclient.*;
apache
import org.apache.commons.httpclient.methods.*;
網絡
import org.apache.commons.httpclient.params.HttpMethodParams;
多線程
import java.io.*;
併發
public class HttpClientTutorial {
ide
private static String url = "http://www.apache.org/";
this
public static void main(String[] args) {
url
// Create an instance of HttpClient.
.net
HttpClient client = new HttpClient();
// Create a method instance.
GetMethod method = new GetMethod(url);
// Provide custom retry handler is necessary
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
try {
// Execute the method.
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
// Read the response body.
byte[] responseBody = method.getResponseBody();
// Deal with the response.
// Use caution: ensure correct character encoding and is not binary data
System.out.println(new String(responseBody));
} catch (HttpException e) {
System.err.println("Fatal protocol violation: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("Fatal transport error: " + e.getMessage());
e.printStackTrace();
} finally {
// Release the connection.
method.releaseConnection();
}
}
}
大部分人也是以這個爲規範來使用的,可是注意有一段關於「Release the Connection」的說明:
This is a crucial step to keep things flowing. We must tell HttpClient that we are done with the connection and that it can now be reused. Without doing this HttpClient will wait indefinitely for a connection to free up so that it can be reused.
我看得也不是很明白,意思是咱們必須在使用後調用
method.releaseConnection();
來告訴HttpClient 這個鏈接能夠重用了。
這個在串行的處理中或許頗有用,可是我所遇到的狀況是多線程併發下,不能共享同一個HttpClient 實例,按照官方示例寫好代碼後,程序跑起來彷佛沒什麼問題,可是隨着時間的累計,有一天忽然發現這個模塊不工做了,查看了一下當前的網絡鏈接,這個java 程序同一個地址保持着200多個CLOSE_WAIT 的鏈接,好吧,鏈接沒有釋放。
爲何沒有釋放?查看doc,有這樣的說明:
Releases the connection being used by this HTTP method. In particular the connection is used to read the response(if there is one) and will be held until the response has been read. If the connection can be reused by other HTTP methods it is NOT closed at this point.
注意最後一句,若是該鏈接能夠重用則不關閉,是「能夠重用」,固然能夠重用了,就在那兒等着我去重用,但是我都是新建的實例,怎麼重用
查看源碼,找到HttpClient 的構造方法,有一個能夠指定HttpConnectionManager ,而後這個HttpConnectionManager 又有一個實現的構造:
public SimpleHttpConnectionManager(boolean alwaysClose)
The connection manager created with this constructor will try to keep the connection open (alive) between consecutive requests if the alwaysClose parameter is set to false. Otherwise the connection manager will always close connections upon release.
Parameters:
alwaysClose - if set true, the connection manager will always close connections upon release.
顯然alawaysClose 的默認值是false ,在釋放後鏈接並不老是會關閉。
因此,必須
HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true));
固然,還有其它的解決方案,找到了一篇文章總結的比較全面:HttpClient容易忽視的細節——鏈接關閉