緊接上一節,本節記錄 【連接池的配置】
兩個主機建立連接的過程是很復雜的一個過程,涉及到多個數據包的交換
,并且也很耗時間。Http連接需要的三次握手
開銷很大,這一開銷對于比較小的http消息來說更大。但是如果我們直接使用已經建立好的http連接,這樣花費就比較小,吞吐率更大。
修改代碼
在上節代碼的基礎上,我們添加全局靜態變量連接池管理對象
,提供靜態代碼塊初始化連接池管理對象.
- 初始化
......前省略
/**
* Http請求工具類
*
* @author 大漠知秋
*/
public class HttpRequestUtils {
/** 全局連接池對象 */
private static final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
/**
* 靜態代碼塊配置連接池信息
*/
static {
// 設置最大連接數
connManager.setMaxTotal(200);
// 設置每個連接的路由數
connManager.setDefaultMaxPerRoute(20);
}
/**
* 獲取Http客戶端連接對象
*
* @param timeOut 超時時間
* @return Http客戶端連接對象
*/
public static CloseableHttpClient getHttpClient(Integer timeOut) {
......后省略
PoolingHttpClientConnectionManager是個復雜的類,它管理著
連接池
,可以同時為很多線程提供http連接請求。當請求一個新的連接時,如果連接池有可用的持久連接,連接管理器就會使用其中的一個,而不是再創建一個新的連接。
PoolingHttpClientConnectionManager維護的連接數在每個路由基礎和總數上都有限制。默認,每個路由基礎上的連接不超過2個,總連接數不能超過20。在實際應用中,這個限制可能會太小了,尤其是當服務器也使用Http協議時。此處我們設置為最高
200
個總連接數和每個基礎路由連接不超過20
個。
- 已經在全局聲明Http連接池管理工具,配置進httpClient連接客戶端
/**
* 獲取Http客戶端連接對象
*
* @param timeOut 超時時間
* @return Http客戶端連接對象
*/
public static CloseableHttpClient getHttpClient(Integer timeOut) {
// 創建Http請求配置參數
RequestConfig requestConfig = RequestConfig.custom()
// 獲取連接超時時間
.setConnectionRequestTimeout(timeOut)
// 請求超時時間
.setConnectTimeout(timeOut)
// 響應超時時間
.setSocketTimeout(timeOut)
.build();
/**
* 測出超時重試機制為了防止超時不生效而設置
* 如果直接放回false,不重試
* 這里會根據情況進行判斷是否重試
*/
HttpRequestRetryHandler retry = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if (executionCount >= 3) {// 如果已經重試了3次,就放棄
return false;
}
if (exception instanceof NoHttpResponseException) {// 如果服務器丟掉了連接,那么就重試
return true;
}
if (exception instanceof SSLHandshakeException) {// 不要重試SSL握手異常
return false;
}
if (exception instanceof InterruptedIOException) {// 超時
return true;
}
if (exception instanceof UnknownHostException) {// 目標服務器不可達
return false;
}
if (exception instanceof ConnectTimeoutException) {// 連接被拒絕
return false;
}
if (exception instanceof SSLException) {// ssl握手異常
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
// 如果請求是冪等的,就再次嘗試
if (!(request instanceof HttpEntityEnclosingRequest)) {
return true;
}
return false;
}
};
// 創建httpClient
return HttpClients.custom()
// 把請求相關的超時信息設置到連接客戶端
.setDefaultRequestConfig(requestConfig)
// 把請求重試設置到連接客戶端
.setRetryHandler(retry)
// 配置連接池管理對象
.setConnectionManager(connManager)
.build();
}
在上方創建httpClient
處把連接池管理對象配置到連接客戶端中
- 最后完整代碼
package com.lynchj.writing;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
/**
* Http請求工具類
*
* @author 大漠知秋
*/
public class HttpRequestUtils {
/** 全局連接池對象 */
private static final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
/**
* 靜態代碼塊配置連接池信息
*/
static {
// 設置最大連接數
connManager.setMaxTotal(200);
// 設置每個連接的路由數
connManager.setDefaultMaxPerRoute(20);
}
/**
* 獲取Http客戶端連接對象
*
* @param timeOut 超時時間
* @return Http客戶端連接對象
*/
public static CloseableHttpClient getHttpClient(Integer timeOut) {
// 創建Http請求配置參數
RequestConfig requestConfig = RequestConfig.custom()
// 獲取連接超時時間
.setConnectionRequestTimeout(timeOut)
// 請求超時時間
.setConnectTimeout(timeOut)
// 響應超時時間
.setSocketTimeout(timeOut)
.build();
/**
* 測出超時重試機制為了防止超時不生效而設置
* 如果直接放回false,不重試
* 這里會根據情況進行判斷是否重試
*/
HttpRequestRetryHandler retry = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if (executionCount >= 3) {// 如果已經重試了3次,就放棄
return false;
}
if (exception instanceof NoHttpResponseException) {// 如果服務器丟掉了連接,那么就重試
return true;
}
if (exception instanceof SSLHandshakeException) {// 不要重試SSL握手異常
return false;
}
if (exception instanceof InterruptedIOException) {// 超時
return true;
}
if (exception instanceof UnknownHostException) {// 目標服務器不可達
return false;
}
if (exception instanceof ConnectTimeoutException) {// 連接被拒絕
return false;
}
if (exception instanceof SSLException) {// ssl握手異常
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
// 如果請求是冪等的,就再次嘗試
if (!(request instanceof HttpEntityEnclosingRequest)) {
return true;
}
return false;
}
};
// 創建httpClient
return HttpClients.custom()
// 把請求相關的超時信息設置到連接客戶端
.setDefaultRequestConfig(requestConfig)
// 把請求重試設置到連接客戶端
.setRetryHandler(retry)
// 配置連接池管理對象
.setConnectionManager(connManager)
.build();
}
/**
* GET請求
*
* @param url 請求地址
* @param timeOut 超時時間
* @return
*/
public static String httpGet(String url, Integer timeOut) {
String msg = "-1";
// 獲取客戶端連接對象
CloseableHttpClient httpClient = getHttpClient(timeOut);
// 創建GET請求對象
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = null;
try {
// 執行請求
response = httpClient.execute(httpGet);
// 獲取響應實體
HttpEntity entity = response.getEntity();
// 獲取響應信息
msg = EntityUtils.toString(entity, "UTF-8");
} catch (ClientProtocolException e) {
System.err.println("協議錯誤");
e.printStackTrace();
} catch (ParseException e) {
System.err.println("解析錯誤");
e.printStackTrace();
} catch (IOException e) {
System.err.println("IO錯誤");
e.printStackTrace();
} finally {
if (null != response) {
try {
EntityUtils.consume(response.getEntity());
response.close();
} catch (IOException e) {
System.err.println("釋放鏈接錯誤");
e.printStackTrace();
}
}
}
return msg;
}
public static void main(String[] args) {
System.out.println(httpGet("http://www.baidu.com", 6000));
}
}
本節完畢,下一節記錄【安全的SSL/TLS連接-繞過證書】