java http連接池的實(shí)現(xiàn)方式(帶有失敗重試等高級(jí)功能)
java 本身提供的java.net.HttpURLConnection不支持連接池功能。
如果不想從頭實(shí)現(xiàn)的話,最好的方式便是引用第三方依賴包,目前是有一個(gè)特別不錯(cuò)的,org.apache.httpcomponents:httpclient
依賴
引入方式如下:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency>
使用httpclient依賴
在開始使用連接池之前,要學(xué)會(huì)如何使用httpclient去完成http請(qǐng)求,其請(qǐng)求方式與java的原生http請(qǐng)求完全不同。
其中CloseableHttpClient
對(duì)象便是我們的http請(qǐng)求連接池,其實(shí)聲明方式會(huì)在下面介紹。
// 引用的包 import org.apache.http.HttpEntity; import org.apache.http.client.methods.*; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DoHttp { private static final Logger LOG = LoggerFactory.getLogger(DoHttp.class); // httpGet請(qǐng)求 public static String get(CloseableHttpClient httpClient, String url) { HttpGet httpGet = new HttpGet(url); return doRequest(httpClient, url, httpGet); } // httpPost請(qǐng)求 (json格式) public static String jsonPost(CloseableHttpClient httpClient, String url, String json) { HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Content-Type", "application/json"); StringEntity entity = new StringEntity(json, "UTF-8"); httpPost.setEntity(entity); return doRequest(httpClient, url, httpPost); } // 統(tǒng)一的請(qǐng)求處理邏輯 private static String doRequest(CloseableHttpClient httpClient, String url, HttpRequestBase httpRequest) { try (CloseableHttpResponse response = httpClient.execute(httpRequest)) { int code = response.getStatusLine().getStatusCode(); HttpEntity responseEntity = response.getEntity(); String responseBody = null; if (responseEntity != null) { responseBody = EntityUtils.toString(responseEntity); } if (code != 200) { LOG.error("http post error, url: {}, code: {}, result: {}", url, code, responseBody); return null; } return responseBody; } catch (Exception e) { LOG.error("http post error, url: {}", url, e); } return null; } }
連接池的實(shí)現(xiàn)
連接池的配置類
如下:
public class HttpPoolConfig { /** http連接池大小 */ public int httpPoolSize; /** http連接超時(shí)時(shí)間 */ public int httpConnectTimeout; /** http連接池等待超時(shí)時(shí)間 */ public int httpWaitTimeout; /** http響應(yīng)包間隔超時(shí)時(shí)間 */ public int httpSocketTimeout; /** http重試次數(shù) */ public int httpRetryCount; /** http重試間隔時(shí)間 */ public int httpRetryInterval; /** http監(jiān)控間隔時(shí)間 定時(shí)清理 打印連接池狀態(tài) */ public int httpMonitorInterval; /** http關(guān)閉空閑連接的等待時(shí)間 */ public int httpCloseIdleConnectionWaitTime; }
連接池實(shí)現(xiàn)類
import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpRequest; import org.apache.http.NoHttpResponseException; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 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.pool.PoolStats; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLException; import java.io.InterruptedIOException; import java.net.UnknownHostException; import java.util.Map; import java.util.TimerTask; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * http連接池 */ public class HttpPool { private static final Logger LOG = LoggerFactory.getLogger(HttpPool.class); /** * 初始化連接池 * @param httpPoolConfig 配置信息 */ public HttpPool(HttpPoolConfig httpPoolConfig) { PoolingHttpClientConnectionManager manager = buildHttpManger(httpPoolConfig); httpClient = buildHttpClient(httpPoolConfig, manager); monitorExecutor = buildMonitorExecutor(httpPoolConfig, manager); } private final CloseableHttpClient httpClient; private final ScheduledExecutorService monitorExecutor; /** * 連接池管理器 */ private PoolingHttpClientConnectionManager buildHttpManger(HttpPoolConfig httpPoolConfig) { LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory(); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("https", sslSocketFactory).build(); PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry); manager.setMaxTotal(httpPoolConfig.httpPoolSize); manager.setDefaultMaxPerRoute(httpPoolConfig.httpPoolSize); return manager; } /** * 建立httpClient */ private CloseableHttpClient buildHttpClient(HttpPoolConfig httpPoolConfig, PoolingHttpClientConnectionManager manager) { // 請(qǐng)求配置 RequestConfig config = RequestConfig.custom() .setConnectTimeout(httpPoolConfig.httpConnectTimeout) .setSocketTimeout(httpPoolConfig.httpSocketTimeout) .setConnectionRequestTimeout(httpPoolConfig.httpWaitTimeout) .build(); // 失敗重試機(jī)制 HttpRequestRetryHandler retryHandler = (e, c, context) -> { if (c > httpPoolConfig.httpRetryCount) { LOG.error("HttpPool request retry more than {} times", httpPoolConfig.httpRetryCount, e); return false; } if (e == null) { LOG.info("HttpPool request exception is null."); return false; } if (e instanceof NoHttpResponseException) { //服務(wù)器沒有響應(yīng),可能是服務(wù)器斷開了連接,應(yīng)該重試 LOG.error("HttpPool receive no response from server, retry"); return true; } // SSL握手異常 if (e instanceof InterruptedIOException // 超時(shí) || e instanceof UnknownHostException // 未知主機(jī) || e instanceof SSLException) { // SSL異常 LOG.error("HttpPool request error, retry", e); return true; } else { LOG.error("HttpPool request unknown error, retry", e); } // 對(duì)于關(guān)閉連接的異常不進(jìn)行重試 HttpClientContext clientContext = HttpClientContext.adapt(context); HttpRequest request = clientContext.getRequest(); return !(request instanceof HttpEntityEnclosingRequest); }; // 構(gòu)建httpClient return HttpClients.custom().setDefaultRequestConfig(config) .setConnectionManager(manager).setRetryHandler(retryHandler).build(); } /** * 建立連接池監(jiān)視器 */ private ScheduledExecutorService buildMonitorExecutor(HttpPoolConfig httpPoolConfig, PoolingHttpClientConnectionManager manager) { TimerTask timerTask = new TimerTask() { @Override public void run() { // 關(guān)閉過期連接 manager.closeExpiredConnections(); // 關(guān)閉空閑時(shí)間超過一定時(shí)間的連接 manager.closeIdleConnections(httpPoolConfig.httpCloseIdleConnectionWaitTime, TimeUnit.MILLISECONDS); // 打印連接池狀態(tài) PoolStats poolStats = manager.getTotalStats(); // max:最大連接數(shù), available:可用連接數(shù), leased:已借出連接數(shù), pending:掛起(表示當(dāng)前等待從連接池中獲取連接的線程數(shù)量) LOG.info("HttpPool status {}", poolStats); } }; ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); int time = httpPoolConfig.httpMonitorInterval; executor.scheduleAtFixedRate(timerTask, time, time, TimeUnit.MILLISECONDS); return executor; } /** * 關(guān)閉連接池 */ public void close() { try { httpClient.close(); monitorExecutor.shutdown(); } catch (Exception e) { LOG.error("HttpPool close http client error", e); } } /** * 發(fā)起get請(qǐng)求 */ public String get(String url) { return DoHttp.get(httpClient, url); } /** * 發(fā)起json格式的post請(qǐng)求 */ public String jsonPost(String url, String json) { return DoHttp.jsonPost(httpClient, url, json); } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot如何返回Json數(shù)據(jù)格式
這篇文章主要介紹了SpringBoot如何返回Json數(shù)據(jù)格式問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03Java編寫簡(jiǎn)單計(jì)算器的完整實(shí)現(xiàn)過程
這篇文章主要給大家介紹了關(guān)于Java編寫簡(jiǎn)單計(jì)算器的完整實(shí)現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Vue3實(shí)現(xiàn)多頁面跳轉(zhuǎn)效果的幾種方式
Vue.js是一個(gè)用于構(gòu)建用戶界面的漸進(jìn)式 JavaScript 框架,它提供了多種方法來實(shí)現(xiàn)頁面之間的導(dǎo)航,在 Vue 3 中,頁面跳轉(zhuǎn)主要通過 Vue Router 來管理,同時(shí)也支持其他方式如編程式導(dǎo)航和使用錨點(diǎn)鏈接,本文將詳細(xì)介紹 Vue 3 中的各種頁面跳轉(zhuǎn)方式,需要的朋友可以參考下2025-03-03java中stringbuffer線程安全分析實(shí)例詳解
在本篇文章里小編給大家整理的是一篇關(guān)于java中stringbuffer線程安全分析實(shí)例詳解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-01-01springcloud下hibernate本地化方言配置方式
這篇文章主要介紹了springcloud下hibernate本地化方言配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Springboot整合quartz產(chǎn)生錯(cuò)誤及解決方案
這篇文章主要介紹了Springboot整合quartz產(chǎn)生錯(cuò)誤及解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06java判斷用戶輸入的是否至少含有N位小數(shù)的實(shí)例
下面小編就為大家分享一篇java判斷用戶輸入的是否至少含有N位小數(shù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12