日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

连接池超时配置_HttpClient连接池的一些思考

發(fā)布時(shí)間:2023/12/9 62 豆豆
生活随笔 收集整理的這篇文章主要介紹了 连接池超时配置_HttpClient连接池的一些思考 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

使用apache的httpclient進(jìn)行http的交互處理已經(jīng)很長(zhǎng)時(shí)間了,而httpclient實(shí)例則使用了http連接池,想必大家也沒(méi)有關(guān)心過(guò)連接池的管理。事實(shí)上,通過(guò)分析httpclient源碼,發(fā)現(xiàn)它很優(yōu)雅地隱藏了所有的連接池管理細(xì)節(jié),開(kāi)發(fā)者完全不用花太多時(shí)間去思考連接池的問(wèn)題。

Apache官網(wǎng)例子

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及其連接池配置

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

MaxtTotal和DefaultMaxPerRoute的區(qū)別

  • MaxtTotal是整個(gè)池子的大小;
  • DefaultMaxPerRoute是根據(jù)連接到的主機(jī)對(duì)MaxTotal的一個(gè)細(xì)分;

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

HttpClient連接池模型

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);//這是每一個(gè)路由細(xì)分出來(lái)的連接池E entry = null;while (entry == null) {Asserts.check(!this.isShutDown, "Connection pool shut down");//從池子中獲取一個(gè)可用連接并返回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;}//創(chuàng)建新的連接// New connection is neededfinal int maxPerRoute = getMax(route);//獲取當(dāng)前路由最大并發(fā)數(shù)// Shrink the pool prior to allocating a new connectionfinal int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);if (excess > 0) {//如果當(dāng)前路由對(duì)應(yīng)的連接池的連接超過(guò)最大路由并發(fā)數(shù),獲取到最后使用的一次連接,釋放掉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);}}//嘗試創(chuàng)建新的連接 if (pool.getAllocatedCount() < maxPerRoute) {//當(dāng)前路由對(duì)應(yīng)的連接池可用空閑連接數(shù)+當(dāng)前路由對(duì)應(yīng)的連接池已用連接數(shù) < 當(dāng)前路由對(duì)應(yīng)的連接池最大并發(fā)數(shù)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) {//線程池中可用空閑連接數(shù) > (線程池中最大連接數(shù) - 線程池中已用連接數(shù) - 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的長(zhǎng)連接復(fù)用, 其判定規(guī)則主要分兩類。
1. http協(xié)議支持+請(qǐng)求/響應(yīng)header指定
2. 一次交互處理的完整性(響應(yīng)內(nèi)容消費(fèi)干凈)
對(duì)于前者, httpclient引入了ConnectionReuseStrategy來(lái)處理, 默認(rèn)的采用如下的約定:

  • HTTP/1.0通過(guò)在Header中添加Connection:Keep-Alive來(lái)表示支持長(zhǎng)連接。
  • HTTP/1.1默認(rèn)支持長(zhǎng)連接, 除非在Header中顯式指定Connection:Close, 才被視為短連接模式。

HttpClientBuilder創(chuàng)建MainClientExec

ConnectionReuseStrategy(連接重用策略)

org.apache.http.impl.client.DefaultClientConnectionReuseStrategy

MainClientExec處理連接

處理完請(qǐng)求后,獲取到response,通過(guò)ConnectionReuseStrategy判斷連接是否可重用,如果是通過(guò)ConnectionKeepAliveStrategy獲取到連接最長(zhǎng)有效時(shí)間,并設(shè)置連接可重用標(biāo)記。

連接重用判斷邏輯

  • request首部中包含Connection:Close,不復(fù)用
  • response中Content-Length長(zhǎng)度設(shè)置不正確,不復(fù)用
  • response首部包含Connection:Close,不復(fù)用
  • reponse首部包含Connection:Keep-Alive,復(fù)用
  • 都沒(méi)命中的情況下,如果HTTP版本高于1.0則復(fù)用

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

連接釋放原理分析

HttpClientBuilder會(huì)構(gòu)建一個(gè)InternalHttpClient實(shí)例,也是CloseableHttpClient實(shí)例。InternalHttpClient的doExecute方法來(lái)完成一次request的執(zhí)行。

會(huì)繼續(xù)調(diào)用MainClientExec的execute方法,通過(guò)連接池管理者獲取連接(HttpClientConnection)。

構(gòu)建ConnectionHolder類型對(duì)象,傳遞連接池管理者對(duì)象和當(dāng)前連接對(duì)象。

請(qǐng)求執(zhí)行完返回HttpResponse類型對(duì)象,然后包裝成HttpResponseProxy對(duì)象(是CloseableHttpResponse實(shí)例)返回。

CloseableHttpClient類其中一個(gè)execute方法如下,finally方法中會(huì)調(diào)用HttpResponseProxy對(duì)象的close方法釋放連接。

最終調(diào)用ConnectionHolder的releaseConnection方法釋放連接。

CloseableHttpClient類另一個(gè)execute方法如下,返回一個(gè)HttpResponseProxy對(duì)象(是CloseableHttpResponse實(shí)例)。

這種情況下調(diào)用者獲取了HttpResponseProxy對(duì)象,可以直接拿到HttpEntity對(duì)象。大家關(guān)心的就是操作完HttpEntity對(duì)象,使用完InputStream到底需不需要手動(dòng)關(guān)閉流呢?

其實(shí)調(diào)用者不需要手動(dòng)關(guān)閉流,因?yàn)镠ttpResponseProxy構(gòu)造方法里有增強(qiáng)HttpEntity的處理方法,如下。

調(diào)用者最終拿到的HttpEntity對(duì)象是ResponseEntityProxy實(shí)例。

ResponseEntityProxy重寫(xiě)了獲取InputStream的方法,返回的是EofSensorInputStream類型的InputStream對(duì)象。

EofSensorInputStream對(duì)象每次讀取都會(huì)調(diào)用checkEOF方法,判斷是否已經(jīng)讀取完畢。

checkEOF方法會(huì)調(diào)用ResponseEntityProxy(實(shí)現(xiàn)了EofSensorWatcher接口)對(duì)象的eofDetected方法。

EofSensorWatcher#eofDetected方法中會(huì)釋放連接并關(guān)閉流。

綜上,通過(guò)CloseableHttpClient實(shí)例處理請(qǐng)求,無(wú)需調(diào)用者手動(dòng)釋放連接。

HttpClient在Spring中應(yīng)用

創(chuàng)建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; }

創(chuàng)建RestTemplate

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

Spring官網(wǎng)例子

@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());};} }

總結(jié)

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

總結(jié)

以上是生活随笔為你收集整理的连接池超时配置_HttpClient连接池的一些思考的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。