使用PoolingHttpClientConnectionManager實(shí)現(xiàn)http連接池過(guò)程
PoolingHttpClientConnectionManager實(shí)現(xiàn)http連接池
PoolingHttpClientConnectionManager是通過(guò)租用連接和收回鏈接的方式來(lái)實(shí)現(xiàn)的。
解決了http請(qǐng)求的多線程問(wèn)題。
依賴(lài)
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>實(shí)現(xiàn)
首先需要初始化連接池。設(shè)定鏈接數(shù)量等,定義清理連接池的方法,并定時(shí)調(diào)用這個(gè)方法清理無(wú)效或者長(zhǎng)期未使用的鏈接。
public class HttpClientTest {
//全局參數(shù)
private static PoolingHttpClientConnectionManager connectionManager = null;
//設(shè)置請(qǐng)求參數(shù)
private RequestConfig config;
private CloseableHttpClient client;
//單例模式創(chuàng)建
private void init(){
synchronized (HttpClientTest.class) {
if (client == null) {
connectionManager = new PoolingHttpClientConnectionManager();
// http請(qǐng)求線程池,最大連接數(shù)
int requestMaxNum = 5000;
ConnectionConfig connConfig = ConnectionConfig.custom().setCharset(Charset.forName("utf-8")).build();
SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(5000).build();
connectionManager.setDefaultConnectionConfig(connConfig);
connectionManager.setDefaultSocketConfig(socketConfig);
// 連接池最大生成連接數(shù)
connectionManager.setMaxTotal(requestMaxNum);
// 默認(rèn)設(shè)置route最大連接數(shù)
connectionManager.setDefaultMaxPerRoute(requestMaxNum);
//設(shè)置請(qǐng)求參數(shù)
config = RequestConfig.custom().setConnectTimeout(5000) //連接超時(shí)時(shí)間
.setConnectionRequestTimeout(500) //從線程池中獲取線程超時(shí)時(shí)間
.setSocketTimeout(5000) //設(shè)置數(shù)據(jù)超時(shí)時(shí)間
.build();
// 創(chuàng)建builder
HttpClientBuilder builder = HttpClients.custom();
//管理器是共享的,它的生命周期將由調(diào)用者管理,并且不會(huì)關(guān)閉
//否則可能出現(xiàn)Connection pool shut down異常
builder.setConnectionManager(connectionManager).setConnectionManagerShared(true);
// 長(zhǎng)連接策略
builder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
// 創(chuàng)建httpClient
client = builder.setDefaultRequestConfig(config).setRetryHandler(new MyRetryHandle()).build();
}
}
}
/**
* 從池子中獲取連接
* @return CloseableHttpClient
*/
private CloseableHttpClient getClientFromHttpPool() {
if(client == null) {
init();
}
return client;
}
}定時(shí)回收鏈接
// 啟動(dòng)定時(shí)器,定時(shí)回收過(guò)期的連接
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 關(guān)閉過(guò)期的鏈接
connectionManager.closeExpiredConnections();
// 選擇關(guān)閉 空閑30秒的鏈接
connectionManager.closeIdleConnections(30, TimeUnit.SECONDS);
}
}, 10 * 1000, 5 * 1000);實(shí)現(xiàn)兩個(gè)方法,執(zhí)行httpqing請(qǐng)求
public String getTest(String url) {
CloseableHttpClient client = getClientFromHttpPool();
HttpGet method = new HttpGet(url);
//設(shè)置請(qǐng)求頭
//method.setRequestHeader();
long start = System.currentTimeMillis();
BufferedReader reader = null;
InputStream inputStream = null;
StringBuilder result = new StringBuilder("");
try {
long startTime = System.currentTimeMillis();
CloseableHttpResponse response = client.execute(method);
if(response.getStatusLine().getStatusCode() == 200){
HttpEntity httpEntity = response.getEntity();
inputStream = httpEntity.getContent();
//解析方式1
reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
String ret = sb.toString();
System.out.println(ret);
//解析方式二
byte[] bytes = new byte[1024];
int length = 0;
while(-1 != (length = inputStream.read(bytes))) {
result.append(new String(bytes, 0, length));
}
}else {
System.out.println("請(qǐng)求失敗,返回" + response.getStatusLine().getStatusCode());
}
System.out.println("用時(shí):" + (System.currentTimeMillis() - startTime));
return result.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(inputStream != null){
try {
inputStream.close();
}catch (Exception e){
}
}
if(reader != null){
try {
reader.close();
}catch (Exception e){
e.printStackTrace();
}
}
//釋放連接
// httpClient必須releaseConnection,但不是abort。因?yàn)閞eleaseconnection是歸還連接到到連接池,而abort是直接拋棄這個(gè)連接,而且占用連接池的數(shù)目。
method.releaseConnection();
}
System.out.println("end..Duration MS:" + (System.currentTimeMillis() - start));
return null;
}
public void postTest(String url, String param) {
CloseableHttpClient client = getClientFromHttpPool();
HttpPost method = new HttpPost(url);
//設(shè)置請(qǐng)求頭
//method.setRequestHeader();
//設(shè)置請(qǐng)求參數(shù)--可以用各種方式傳入?yún)?shù)
try {
method.setEntity(new StringEntity(param));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
long start = System.currentTimeMillis();
BufferedReader reader = null;
InputStream inputStream = null;
try {
CloseableHttpResponse response = client.execute(method);
if(response.getStatusLine().getStatusCode() == 200){
HttpEntity httpEntity = response.getEntity();
inputStream = httpEntity.getContent();
//解析方式1
reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
String ret = sb.toString();
System.out.println(ret);
//解析方式二
byte[] bytes = new byte[1024];
int length = 0;
StringBuilder result = new StringBuilder("");
while(-1 != (length = inputStream.read(bytes))) {
result.append(new String(bytes, 0, length));
}
System.out.println(result);
}else {
System.out.println("請(qǐng)求失敗,返回" + response.getStatusLine().getStatusCode());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(inputStream != null){
try {
inputStream.close();
}catch (Exception e){
}
}
if(reader != null){
try {
reader.close();
}catch (Exception e){
}
}
//關(guān)閉池子
/* if(client != null){
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}*/
//釋放連接
method.releaseConnection();
}
System.out.println("end..Duration MS:" + (System.currentTimeMillis() - start));
}重試策略
加入重試策略:
// 創(chuàng)建httpClient
return builder.setDefaultRequestConfig(config).setRetryHandler(new MyRetryHandle()).build();/**
* 請(qǐng)求連接池失敗重試策略
*/
public class MyRetryHandle implements HttpRequestRetryHandler {
Logger logger = LoggerFactory.getLogger(MyRetryHandle.class);
//請(qǐng)求失敗時(shí),進(jìn)行請(qǐng)求重試
@Override
public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
if (i > 3) {
//重試超過(guò)3次,放棄請(qǐng)求
logger.error("retry has more than 3 time, give up request");
return false;
}
if (e instanceof NoHttpResponseException) {
//服務(wù)器沒(méi)有響應(yīng),可能是服務(wù)器斷開(kāi)了連接,應(yīng)該重試
logger.error("receive no response from server, retry");
return true;
}
if (e instanceof SSLHandshakeException) {
// SSL握手異常
logger.error("SSL hand shake exception");
return false;
}
if (e instanceof InterruptedIOException) {
//超時(shí)
logger.error("InterruptedIOException");
return false;
}
if (e instanceof UnknownHostException) {
// 服務(wù)器不可達(dá)
logger.error("server host unknown");
return false;
}
if (e instanceof ConnectTimeoutException) {
// 連接超時(shí)
logger.error("Connection Time out");
return false;
}
if (e instanceof SSLException) {
logger.error("SSLException");
return false;
}
HttpClientContext context = HttpClientContext.adapt(httpContext);
HttpRequest request = context.getRequest();
if (!(request instanceof HttpEntityEnclosingRequest)) {
//如果請(qǐng)求不是關(guān)閉連接的請(qǐng)求
return true;
}
return false;
}
}原理及注意事項(xiàng)
連接池中連接都是在發(fā)起請(qǐng)求的時(shí)候建立,并且都是長(zhǎng)連接
HaoMaiClient.java中的in.close();作用就是將用完的連接釋放,下次請(qǐng)求可以復(fù)用,這里特別注意的是,如果不使用in.close();而僅僅使用response.close();結(jié)果就是連接會(huì)被關(guān)閉,并且不能被復(fù)用,這樣就失去了采用連接池的意義。
連接池釋放連接的時(shí)候,并不會(huì)直接對(duì)TCP連接的狀態(tài)有任何改變,只是維護(hù)了兩個(gè)Set,leased和avaliabled,leased代表被占用的連接集合,avaliabled代表可用的連接的集合,釋放連接的時(shí)候僅僅是將連接從leased中remove掉了,并把連接放到avaliabled集合中
http://hc.apache.org/ 文檔以及源碼
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud微服務(wù)續(xù)約實(shí)現(xiàn)源碼分析詳解
這篇文章主要介紹了SpringCloud微服務(wù)續(xù)約實(shí)現(xiàn)源碼分析,服務(wù)續(xù)期和服務(wù)注冊(cè)非常相似,服務(wù)注冊(cè)在Eureka?Client程序啟動(dòng)之后開(kāi)啟,并同時(shí)開(kāi)啟服務(wù)續(xù)期的定時(shí)任務(wù)2022-11-11
Java面向?qū)ο蠡A(chǔ)知識(shí)之封裝,繼承,多態(tài)和抽象
這篇文章主要介紹了Java面向?qū)ο蟮姆庋b,繼承,多態(tài)和抽象,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下2021-11-11
IDEA無(wú)法識(shí)別SpringBoot項(xiàng)目的簡(jiǎn)單解決辦法
今天使用idea的時(shí)候,遇到idea無(wú)法啟動(dòng)springboot,所以這篇文章主要給大家介紹了關(guān)于IDEA無(wú)法識(shí)別SpringBoot項(xiàng)目的簡(jiǎn)單解決辦法,需要的朋友可以參考下2023-08-08
java發(fā)送get請(qǐng)求和post請(qǐng)求示例
這篇文章主要介紹了java發(fā)送get請(qǐng)求和post請(qǐng)求示例,需要的朋友可以參考下2014-03-03
PowerJob的DispatchStrategy方法工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的DispatchStrategy方法工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01

