連接池超時配置_HttpClient連接池的一些思考

79cd0f952e78876a89ade4958e690871.png

前言

使用apache的httpclient進行http的交互處理已經很長時間了,而httpclient實例則使用了http連接池,想必大家也沒有關心過連接池的管理。事實上,通過分析httpclient源碼,發現它很優雅地隱藏了所有的連接池管理細節,開發者完全不用花太多時間去思考連接池的問題。

Apache官網例子

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {HttpEntity entity = response.getEntity();if (entity != null) {long len = entity.getContentLength();if (len != -1 && len < 2048) {System.out.println(EntityUtils.toString(entity));} else {// Stream content out}}
} finally {response.close();
}

HttpClient及其連接池配置

  • 整個線程池中最大連接數 MAX_CONNECTION_TOTAL = 800
  • 路由到某臺主機最大并發數,是MAX_CONNECTION_TOTAL(整個線程池中最大連接數)的一個細分 ROUTE_MAX_COUNT = 500
  • 重試次數,防止失敗情況 RETRY_COUNT = 3
  • 客戶端和服務器建立連接的超時時間 CONNECTION_TIME_OUT = 5000
  • 客戶端從服務器讀取數據的超時時間 READ_TIME_OUT = 7000
  • 從連接池中獲取連接的超時時間 CONNECTION_REQUEST_TIME_OUT = 5000
  • 連接空閑超時,清楚閑置的連接 CONNECTION_IDLE_TIME_OUT = 5000
  • 連接保持存活時間 DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000

MaxtTotal和DefaultMaxPerRoute的區別

  • MaxtTotal是整個池子的大小;
  • DefaultMaxPerRoute是根據連接到的主機對MaxTotal的一個細分;

比如:MaxtTotal=400,DefaultMaxPerRoute=200,而我只連接到http://hjzgg.com時,到這個主機的并發最多只有200;而不是400;而我連接到http://qyxjj.com 和 http://httls.com時,到每個主機的并發最多只有200;即加起來是400(但不能超過400)。所以起作用的設置是DefaultMaxPerRoute。

HttpClient連接池模型

91ec13803b9936c0c0c78a7c582454cf.png

HttpClient從連接池中獲取連接分析

org.apache.http.pool.AbstractConnPool

private E getPoolEntryBlocking(final T route, final Object state,final long timeout, final TimeUnit tunit,final PoolEntryFuture<E> future)throws IOException, InterruptedException, TimeoutException {Date deadline = null;if (timeout > 0) {deadline = new Date(System.currentTimeMillis() + tunit.toMillis(timeout));}this.lock.lock();try {final RouteSpecificPool<T, C, E> pool = getPool(route);//這是每一個路由細分出來的連接池E entry = null;while (entry == null) {Asserts.check(!this.isShutDown, "Connection pool shut down");//從池子中獲取一個可用連接并返回for (;;) {entry = pool.getFree(state);if (entry == null) {break;}if (entry.isExpired(System.currentTimeMillis())) {entry.close();} else if (this.validateAfterInactivity > 0) {if (entry.getUpdated() + this.validateAfterInactivity <= System.currentTimeMillis()) {if (!validate(entry)) {entry.close();}}}if (entry.isClosed()) {this.available.remove(entry);pool.free(entry, false);} else {break;}}if (entry != null) {this.available.remove(entry);this.leased.add(entry);onReuse(entry);return entry;}//創建新的連接// New connection is neededfinal int maxPerRoute = getMax(route);//獲取當前路由最大并發數// Shrink the pool prior to allocating a new connectionfinal int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);if (excess > 0) {//如果當前路由對應的連接池的連接超過最大路由并發數,獲取到最后使用的一次連接,釋放掉for (int i = 0; i < excess; i++) {final E lastUsed = pool.getLastUsed();if (lastUsed == null) {break;}lastUsed.close();this.available.remove(lastUsed);pool.remove(lastUsed);}}//嘗試創建新的連接 if (pool.getAllocatedCount() < maxPerRoute) {//當前路由對應的連接池可用空閑連接數+當前路由對應的連接池已用連接數 < 當前路由對應的連接池最大并發數final int totalUsed = this.leased.size();final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);if (freeCapacity > 0) {final int totalAvailable = this.available.size();if (totalAvailable > freeCapacity - 1) {//線程池中可用空閑連接數 > (線程池中最大連接數 - 線程池中已用連接數 - 1)if (!this.available.isEmpty()) {final E lastUsed = this.available.removeLast();lastUsed.close();final RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());otherpool.remove(lastUsed);}}final C conn = this.connFactory.create(route);entry = pool.add(conn);this.leased.add(entry);return entry;}}boolean success = false;try {pool.queue(future);this.pending.add(future);success = future.await(deadline);} finally {// In case of 'success', we were woken up by the// connection pool and should now have a connection// waiting for us, or else we're shutting down.// Just continue in the loop, both cases are checked.pool.unqueue(future);this.pending.remove(future);}// check for spurious wakeup vs. timeoutif (!success && (deadline != null) &&(deadline.getTime() <= System.currentTimeMillis())) {break;}}throw new TimeoutException("Timeout waiting for connection");} finally {this.lock.unlock();}
}

連接重用和保持策略

http的長連接復用, 其判定規則主要分兩類。
1. http協議支持+請求/響應header指定
2. 一次交互處理的完整性(響應內容消費干凈)
對于前者, httpclient引入了ConnectionReuseStrategy來處理, 默認的采用如下的約定:

  • HTTP/1.0通過在Header中添加Connection:Keep-Alive來表示支持長連接。
  • HTTP/1.1默認支持長連接, 除非在Header中顯式指定Connection:Close, 才被視為短連接模式。

HttpClientBuilder創建MainClientExec

24521f7681e948f45bd281e2c8ec2f33.png

ConnectionReuseStrategy(連接重用策略)

org.apache.http.impl.client.DefaultClientConnectionReuseStrategy

b8f84e94e51301db9aa55d081617ba80.png

MainClientExec處理連接

處理完請求后,獲取到response,通過ConnectionReuseStrategy判斷連接是否可重用,如果是通過ConnectionKeepAliveStrategy獲取到連接最長有效時間,并設置連接可重用標記。

dfb425780e422bc3f543e4ca75b81536.png

連接重用判斷邏輯

  • request首部中包含Connection:Close,不復用
  • response中Content-Length長度設置不正確,不復用
  • response首部包含Connection:Close,不復用
  • reponse首部包含Connection:Keep-Alive,復用
  • 都沒命中的情況下,如果HTTP版本高于1.0則復用

更多參考:https://www.cnblogs.com/mumuxinfei/p/9121829.html

連接釋放原理分析

HttpClientBuilder會構建一個InternalHttpClient實例,也是CloseableHttpClient實例。InternalHttpClient的doExecute方法來完成一次request的執行。

2755c8b433083349b54c1455bc91f4cb.png

會繼續調用MainClientExec的execute方法,通過連接池管理者獲取連接(HttpClientConnection)。

1387916efb9da061f4e2862b55c8369d.png

構建ConnectionHolder類型對象,傳遞連接池管理者對象和當前連接對象。

234adfbd2974a40c7864c8173b058d5a.png

請求執行完返回HttpResponse類型對象,然后包裝成HttpResponseProxy對象(是CloseableHttpResponse實例)返回。

eec21e8726f45d1d0427807d024c027b.png

CloseableHttpClient類其中一個execute方法如下,finally方法中會調用HttpResponseProxy對象的close方法釋放連接。

0b180503254387eb9e286f325f138d96.png

a314ba3e7f7b99c250e19b0cbe35ce05.png

最終調用ConnectionHolder的releaseConnection方法釋放連接。

a85b0c272aada8ba2be77f98debe4a2a.png

414b2b99a37ac7e8ee9157af8fbfa4db.png

CloseableHttpClient類另一個execute方法如下,返回一個HttpResponseProxy對象(是CloseableHttpResponse實例)。

6c270f9ee337d52673c921e23c082baf.png

這種情況下調用者獲取了HttpResponseProxy對象,可以直接拿到HttpEntity對象。大家關心的就是操作完HttpEntity對象,使用完InputStream到底需不需要手動關閉流呢?

a366202abcb42c8ff3140d42ffc7809d.png

其實調用者不需要手動關閉流,因為HttpResponseProxy構造方法里有增強HttpEntity的處理方法,如下。

a86e3abd2ba30aaf274f6765c4fb842b.png

調用者最終拿到的HttpEntity對象是ResponseEntityProxy實例。

cb4ea1e4822a29d0da7923a27437443b.png

ResponseEntityProxy重寫了獲取InputStream的方法,返回的是EofSensorInputStream類型的InputStream對象。

63439c90dd77c27eaa58c20e9afc7555.png

EofSensorInputStream對象每次讀取都會調用checkEOF方法,判斷是否已經讀取完畢。

a1c7026607b6fe9fba15bd97e579c709.png

checkEOF方法會調用ResponseEntityProxy(實現了EofSensorWatcher接口)對象的eofDetected方法。

bd425a6b5ef7542cc26f5f2e7bffa26a.png

EofSensorWatcher#eofDetected方法中會釋放連接并關閉流。

56f3a3762f8fb9fd61a3264926020bf5.png

綜上,通過CloseableHttpClient實例處理請求,無需調用者手動釋放連接。

HttpClient在Spring中應用

創建ClientHttpRequestFactory

@Bean
public ClientHttpRequestFactory clientHttpRequestFactory()throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();httpClientBuilder.setSSLContext(sslContext).setMaxConnTotal(MAX_CONNECTION_TOTAL).setMaxConnPerRoute(ROUTE_MAX_COUNT).evictIdleConnections(CONNECTION_IDLE_TIME_OUT, TimeUnit.MILLISECONDS);httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(RETRY_COUNT, true));httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());CloseableHttpClient client = httpClientBuilder.build();HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);clientHttpRequestFactory.setConnectTimeout(CONNECTION_TIME_OUT);clientHttpRequestFactory.setReadTimeout(READ_TIME_OUT);clientHttpRequestFactory.setConnectionRequestTimeout(CONNECTION_REQUEST_TIME_OUT);clientHttpRequestFactory.setBufferRequestBody(false);return clientHttpRequestFactory;
}

創建RestTemplate

@Bean
public RestTemplate restTemplate()  {RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());restTemplate.setErrorHandler(new DefaultResponseErrorHandler());// 修改StringHttpMessageConverter內容轉換器restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));return restTemplate;
}

Spring官網例子

@SpringBootApplication
public class Application {private static final Logger log = LoggerFactory.getLogger(Application.class);public static void main(String args[]) {SpringApplication.run(Application.class);}@Beanpublic RestTemplate restTemplate(RestTemplateBuilder builder) {return builder.build();}@Beanpublic CommandLineRunner run(RestTemplate restTemplate) throws Exception {return args -> {Quote quote = restTemplate.getForObject("https://gturnquist-quoters.cfapps.io/api/random", Quote.class);log.info(quote.toString());};}
}

總結

Apache的HttpClient組件可謂良心之作,細細的品味一下源碼可以學到很多設計模式和比編碼規范。不過在閱讀源碼之前最好了解一下不同版本的HTTP協議,尤其是HTTP協議的Keep-Alive模式。使用Keep-Alive模式(又稱持久連接、連接重用)時,Keep-Alive功能使客戶端到服 務器端的連接持續有效,當出現對服務器的后繼請求時,Keep-Alive功能避免了建立或者重新建立連接。這里推薦一篇參考鏈接:https://www.jianshu.com/p/49551bda6619。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/397836.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397836.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397836.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

android界面布局錯位,IOS 瀏覽器頁面布局錯位(如:點不到)的分析與解決

IOS 瀏覽器頁面布局錯位(如&#xff1a;點不到)的分析與解決IOS 瀏覽器軟鍵盤的拉起與收縮、微信 IOS 瀏覽器底部導航條的顯示與隱藏&#xff0c;很容易導致頁面布局錯位(相對窗體的絕對定位元素)&#xff1a;明明按鈕在這里&#xff0c;卻要在上面一點兒點擊屏幕才能點到它明明…

做進度條 根據自己的數據顯示進度

做了很多種方法 1&#xff1a; 線程 thread的方法 2&#xff1a; backGroundWorker的方法 3&#xff1a; 自定義線程類 4&#xff1a; 做一個進度條的窗體 通過自定義設置做&#xff08;最方便快捷&#xff09; public partial class waitingProcessbar : Form{public waitin…

視圖和模型變換

視圖變換&#xff0c;是指變換照相機的位置&#xff0c;角度。 模型變換&#xff0c;是指變換被照物體的位置&#xff0c;角度。 這兩個變換&#xff0c;都會影響最終圖形中&#xff0c;物體的位置&#xff0c;角度。而這兩個變換&#xff0c;可以達到相同的效果。比如&#x…

phoenix的元數據一般存在哪里_Phoenix的一些問題

date: 2020-09-10 13:50:00updated: 2020-09-14 16:30:001. Phoenix索引全局索引&#xff1a;適合讀多寫少的場景。寫數據時因為索引表分布在不同數據節點&#xff0c;跨節點數據傳輸帶來巨大的性能消耗。全局索引必須是查詢語句中所有列都包含在全局索引中&#xff0c;它才會生…

鴻蒙os全面升級,華為突然宣布,鴻蒙OS正式版6月底全面升級,幸福來得太突然...

原標題&#xff1a;華為突然宣布&#xff0c;鴻蒙OS正式版6月底全面升級&#xff0c;幸福來得太突然摘要&#xff1a;早在今年2月華為Mate X2折疊屏新品發布會上&#xff0c;余承東曾表示&#xff0c;鴻蒙OS正式版將于今年4月份全面上線。或許是因為華為宣布賣車分散了很多的精…

5-python學習——條件語句

5-python學習——條件語句 5-python學習——條件語句 條件語句if else形式if else條件語句說明 測試一下編程語言一般都由這么幾個部分組成 變量條件分支語句循環語句函數這里要說的就是條件分支語句。 python的條件語句和shell腳本的非常像&#xff0c;也就是if else if else這…

eclipse啟動失敗:An internal error occurred during: reload maven project

2019獨角獸企業重金招聘Python工程師標準>>> 1.找到workspace文件夾下的/.metadata文件夾&#xff0c;將其刪除掉&#xff0c;然后在講項目重新導入進去eclipse中。但是這個有一點不好的地方&#xff0c;之前對eclipse所做的配置也會恢復為默認配置 2.在.metadata下…

Quartz2D知識點聚合案例

Quartz2D知識點聚合 基本 //畫圖片UIImage *image [UIImage imageNamed:"阿貍頭像"];[image drawInRect:rect];//字體NSString *title "標題";NSMutableDictionary *atr [NSMutableDictionary dictionary];atr[NSFontAttributeName] [UIFont systemFon…

skt7850鴻蒙策略,lol 英雄聯盟 SKT狀態回暖輕取外卡,SUP難擋Faker

MSI 第四日 SUP vs SKT雙方bpBAN LISTBAN&#xff1a;SUP:流浪 牛頭 豹女SKT&#xff1a;巴德 妖姬 魚人PICKSUP:大樹 男槍 冰女 盧錫安 錘石SKT&#xff1a;艾克 千玨 沙皇 EZ 布隆比賽開始&#xff0c;雙方正常對線開局。前期下路錘石多次勾中ez&#xff0c;男槍也來逼出EZ布…

spring集成struts2

Struts2前身是WebWork&#xff0c;核心并沒有改變&#xff0c;其實就是把WebWork改名為struts2&#xff0c;與Struts1一點關系沒有。 Struts2中通過ObjectFactory接口實現創建及獲取Action實例&#xff0c;類似于Spring的IoC容器&#xff0c;所以Action實例可以由ObjectFactory…

slqite3庫查詢數據處理方式_SQLite3命令操作大全

SQLite3命令操作大全SQLite庫包含一個名字叫做sqlite3的命令行,它可以讓用戶手工輸入并執行面向SQLite數據庫的SQL命令。本文檔提供一個樣使用sqlite3的簡要說明.一.qlite3一些常用Sql語句操作創建表: create table 表名(元素名 類型,…);刪除表: drop …

Android學習之查看網絡圖片

在這里小編學習了查看網絡圖片的小案例,: 初始界面: 點擊瀏覽后,效果如下: 需要注意的是 該案例需要獲取聯網權限,即: <uses-permission android:name"android.permission.INTERNET"/>具體步驟如下: 1.定義并初始化控件: private EditText etImageUrl;private …

AutoLayout 淺析動畫

1.AutoLayout相關的幾個易混淆的方法 setNeedsLayout layoutIfNeeded layoutSubViews setNeedsUpdateConstraints updateConstraitsIfNeed updateConstraints 子視圖在界面上的顯示大概經過了&#xff1a;更新約束-通過約束依賴關系得到具體的frame-展示到界面。上面幾個是和au…

vue 轉為靜態html,Vue CLI 3使用:HTML和靜態資源(五)

HTMLpublic/index.html 文件是一個會被 html-webpack-plugin 處理的模板。構建中&#xff0c;各種資源路徑會被注入解析。可以使用 lodash template 語法插入內容。用來做不轉義插值&#xff1b;用來做 HTML 轉義插值&#xff1b;用來描述 JavaScript 流程控制。除了被 html-we…

animate css3 應用的借鑒,一個同事寫的JS

$("#banner").height($(window).height()-125);$(window).resize(function(){ $("#banner").height($(window).height()-125);}); //首頁幻燈$(".indeximgs:first").show();var i0;$(".leftbut").click(function(){$(".indexim…

從html導出帶樣式的excel,Jquery導出帶樣式的Excel

工作中做導出的時候&#xff0c;需要導出自定義的表格或嫌棄導出的Excel格式太難看了。需要設置顏色、字號大小、加粗、合并單元格等等。特性&#xff1a;支持過濾 某個位置支持過濾 img 標簽支持過濾 a 標簽支持過濾 input 標簽支持包含 行內樣式。HTML頁面&#xff1a;HTML頁…

elementui from表單提交_elementui upload與form一起提交

學生基本信息管理操作中&#xff0c;有照片&#xff0c;可以上傳也可以不上傳&#xff0c;在表單界面可以修改照片&#xff0c;el-upload控件可以帶額外參數提交&#xff0c;jquery的post模擬不了成表單帶文件提交的方式&#xff0c;因此&#xff0c;判斷如果有上傳文件時&…

information_schema.character_sets 學習

information_schema.character_sets 表用于查看字符集的詳細信息 1、character_sets 常用列說明&#xff1a; 1、character_set_name&#xff1a;    字符集名 2、default_collate_name&#xff1a;    默認排序規則   3、description&#xff1a;         …

asp.net mvc 用Redis實現分布式集群共享Session。

1、這兩天研究Redis搞分布式session問題&#xff0c;網上找的資料都是用ServiceStack.Redis來實現的&#xff0c;但是在做性能測試的時候發現最新的v4版本有限制每小時候最多請求6000次&#xff0c;因為官網開始商業化要收費了&#xff0c;好坑爹的說&#xff0c;還好我前期弄了…

如何用計算機求和,求和計算器

求和計算器您可以使用此求和計算器快速計算預定范圍內某個表達式的序列之和。如何使用求和計算器輸入總和的表達式輸入上限和下限提供表達式中使用的變量的詳細信息單擊“計算”按鈕生成結果。求和Σ符號計算器k ∑n 0變量:nixyzabc 103740支持的運算符常量和函數算術運算符加“…