亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

spring boot封裝HttpClient的示例代碼

2024-07-13 10:16:49
字體:
來源:轉載
供稿:網友

最近使用到了HttpClient,看了一下官方文檔:HttpClient implementations are expected to be thread safe. It is recommended that the same instance of this class is reused for multiple request executions,翻譯過來的意思就是:HttpClient的實現是線程安全的,可以重用相同的實例來執行多次請求。遇到這種描述的話,我們就應該想到,需要對HttpClient來進行封裝了。由于是使用的spring boot,所以下面來結合spring boot來封裝HttpClient。

一、Request retry handler(請求重試處理)

為了使自定義異常機制生效,需要實現HttpRequestRetryHandler接口,代碼如下:

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.HttpEntityEnclosingRequest; import org.apache.http.HttpRequest; import org.apache.http.NoHttpResponseException; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.protocol.HttpContext; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 描述:HttpClient的重試處理機制  */ @Configuration public class MyhttpRequestRetryHandler {    @Value("${httpclient.config.retryTime}")// 此處建議采用@ConfigurationProperties(prefix="httpclient.config")方式,方便復用   private int retryTime;      @Bean   public HttpRequestRetryHandler httpRequestRetryHandler() {     // 請求重試     final int retryTime = this.retryTime;     return new HttpRequestRetryHandler() {       public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {         // Do not retry if over max retry count,如果重試次數超過了retryTime,則不再重試請求         if (executionCount >= retryTime) {           return false;         }         // 服務端斷掉客戶端的連接異常         if (exception instanceof NoHttpResponseException) {           return true;         }         // time out 超時重試         if (exception instanceof InterruptedIOException) {           return true;         }         // Unknown host         if (exception instanceof UnknownHostException) {           return false;         }         // Connection refused         if (exception instanceof ConnectTimeoutException) {           return false;         }         // SSL handshake exception         if (exception instanceof SSLException) {           return false;         }         HttpClientContext clientContext = HttpClientContext.adapt(context);         HttpRequest request = clientContext.getRequest();         if (!(request instanceof HttpEntityEnclosingRequest)) {           return true;         }         return false;       }     };   } } 

二、Pooling connection manager(連接池管理)

PoolingHttpClientConnectionManager用來管理客戶端的連接池,并且可以為多個線程的請求提供服務,代碼如下:

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.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyPoolingHttpClientConnectionManager {   /**    * 連接池最大連接數    */   @Value("${httpclient.config.connMaxTotal}")   private int connMaxTotal = 20;      /**    *    */   @Value("${httpclient.config.maxPerRoute}")   private int maxPerRoute = 20;      /**    * 連接存活時間,單位為s    */    @Value("${httpclient.config.timeToLive}")    private int timeToLive = 60;      @Bean   public PoolingHttpClientConnectionManager poolingClientConnectionManager(){     PoolingHttpClientConnectionManager poolHttpcConnManager = new PoolingHttpClientConnectionManager(60, TimeUnit.SECONDS);     // 最大連接數     poolHttpcConnManager.setMaxTotal(this.connMaxTotal);     // 路由基數     poolHttpcConnManager.setDefaultMaxPerRoute(this.maxPerRoute);     return poolHttpcConnManager;   } } 

注意:當HttpClient實例不再需要并且即將超出范圍時,重要的是關閉其連接管理器,以確保管理器保持活動的所有連接都被關閉,并釋放由這些連接分配的系統資源

上面PoolingHttpClientConnectionManager類的構造函數如下:

public PoolingHttpClientConnectionManager(final long timeToLive, final TimeUnit tunit) {     this(getDefaultRegistry(), null, null ,null, timeToLive, tunit);   }  private static Registry<ConnectionSocketFactory> getDefaultRegistry() {     return RegistryBuilder.<ConnectionSocketFactory>create()         .register("http", PlainConnectionSocketFactory.getSocketFactory())         .register("https", SSLConnectionSocketFactory.getSocketFactory())         .build();   } 

在PoolingHttpClientConnectionManager的配置中有兩個最大連接數量,分別控制著總的最大連接數量和每個route的最大連接數量。如果沒有顯式設置,默認每個route只允許最多2個connection,總的connection數量不超過20。這個值對于很多并發度高的應用來說是不夠的,必須根據實際的情況設置合適的值,思路和線程池的大小設置方式是類似的,如果所有的連接請求都是到同一個url,那可以把MaxPerRoute的值設置成和MaxTotal一致,這樣就能更高效地復用連接

特別注意:想要復用一個connection就必須要讓它占有的系統資源得到正確釋放,釋放方法如下:

如果是使用outputStream就要保證整個entity都被write out,如果是inputStream,則再最后要記得調用inputStream.close()。或者使用EntityUtils.consume(entity)或EntityUtils.consumeQuietly(entity)來讓entity被完全耗盡(后者不拋異常)來做這一工作。EntityUtils中有個toString方法也很方便的(調用這個方法最后也會自動把inputStream close掉的,但是在實際的測試過程中,會導致連接沒有釋放的現象),不過只有在可以確定收到的entity不是特別大的情況下才能使用。如果沒有讓整個entity被fully consumed,則該連接是不能被復用的,很快就會因為在連接池中取不到可用的連接超時或者阻塞在這里(因為該連接的狀態將會一直是leased的,即正在被使用的狀態)。所以如果想要復用connection,一定一定要記得把entity fully consume掉,只要檢測到stream的eof,是會自動調用ConnectionHolder的releaseConnection方法進行處理的

三、Connection keep alive strategy(保持連接策略)

HTTP規范沒有指定持久連接可能和應該保持存活多久。一些HTTP服務器使用非標準的Keep-Alive標頭來向客戶端通信它們打算在服務器端保持連接的時間段(以秒為單位)。HttpClient可以使用這些信息。如果響應中不存在Keep-Alive頭,HttpClient會假定連接可以無限期地保持活動。然而,一般使用的許多HTTP服務器都配置為在一段不活動狀態之后刪除持久連接,以便節省系統資源,而不會通知客戶端。如果默認策略過于樂觀,則可能需要提供自定義的保持活動策略,代碼如下:

import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HttpResponse; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 描述:連接保持策略  * @author chhliu  */ @Configuration public class MyconnectionKeepAliveStrategy {      @Value("${httpclient.config.keepAliveTime}")   private int keepAliveTime = 30;      @Bean("connectionKeepAliveStrategy")   public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {     return new ConnectionKeepAliveStrategy() {        public long getKeepAliveDuration(HttpResponse response, HttpContext context) {         // Honor 'keep-alive' header         HeaderElementIterator it = new BasicHeaderElementIterator(             response.headerIterator(HTTP.CONN_KEEP_ALIVE));         while (it.hasNext()) {           HeaderElement he = it.nextElement();           String param = he.getName();           String value = he.getValue();           if (value != null && param.equalsIgnoreCase("timeout")) {             try {               return Long.parseLong(value) * 1000;             } catch (NumberFormatException ignore) {             }           }         }         return 30 * 1000;       }     };   } } 

注意:長連接并不使用于所有的情況,尤其現在的系統,大都是部署在多臺服務器上,且具有負載均衡的功能,如果我們在訪問的時候,一直保持長連接,一旦那臺服務器掛了,就會影響客戶端,同時也不能充分的利用服務端的負載均衡的特性,反而短連接更有利一些,這些需要根據具體的需求來定,而不是一言概括。

四、HttpClient proxy configuration(代理配置)

用來配置代理,代碼如下:

import org.apache.http.HttpHost; import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /**  * 描述:HttpClient代理  * @author chhliu  */ @Configuration public class MyDefaultProxyRoutePlanner {   // 代理的host地址   @Value("${httpclient.config.proxyhost}")   private String proxyHost;      // 代理的端口號   @Value("${httpclient.config.proxyPort}")   private int proxyPort = 8080;      @Bean   public DefaultProxyRoutePlanner defaultProxyRoutePlanner(){     HttpHost proxy = new HttpHost(this.proxyHost, this.proxyPort);     return new DefaultProxyRoutePlanner(proxy);   } } 

HttpClient不僅支持簡單的直連、復雜的路由策略以及代理。HttpRoutePlanner是基于http上下文情況下,客戶端到服務器的路由計算策略,一般沒有代理的話,就不用設置這個東西。這里有一個很關鍵的概念—Route:在HttpClient中,一個Route指 運行環境機器->目標機器host的一條線路,也就是如果目標url的host是同一個,那么它們的route也是一樣的

五、RequestConfig

用來設置請求的各種配置,代碼如下:

import org.apache.http.client.config.RequestConfig; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  @Configuration public class MyRequestConfig {   @Value("${httpclient.config.connectTimeout}")   private int connectTimeout = 2000;      @Value("${httpclient.config.connectRequestTimeout}")   private int connectRequestTimeout = 2000;      @Value("${httpclient.config.socketTimeout}")   private int socketTimeout = 2000;   @Bean   public RequestConfig config(){     return RequestConfig.custom()         .setConnectionRequestTimeout(this.connectRequestTimeout)         .setConnectTimeout(this.connectTimeout)         .setSocketTimeout(this.socketTimeout)         .build();   } } 

RequestConfig是對request的一些配置。里面比較重要的有三個超時時間,默認的情況下這三個超時時間都為0(如果不設置request的Config,會在execute的過程中使用HttpClientParamConfig的getRequestConfig中用默認參數進行設置),這也就意味著無限等待,很容易導致所有的請求阻塞在這個地方無限期等待。這三個超時時間為:

a、connectionRequestTimeout—從連接池中取連接的超時時間

這個時間定義的是從ConnectionManager管理的連接池中取出連接的超時時間, 如果連接池中沒有可用的連接,則request會被阻塞,最長等待connectionRequestTimeout的時間,如果還沒有被服務,則拋出ConnectionPoolTimeoutException異常,不繼續等待。

b、connectTimeout—連接超時時間

這個時間定義了通過網絡與服務器建立連接的超時時間,也就是取得了連接池中的某個連接之后到接通目標url的連接等待時間。發生超時,會拋出ConnectionTimeoutException異常。

c、socketTimeout—請求超時時間

這個時間定義了socket讀數據的超時時間,也就是連接到服務器之后到從服務器獲取響應數據需要等待的時間,或者說是連接上一個url之后到獲取response的返回等待時間。發生超時,會拋出SocketTimeoutException異常。

六、實例化HttpClient

通過實現FactoryBean來實例化HttpClient,代碼如下:

import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.config.RequestConfig; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;  /**  * 描述:HttpClient客戶端封裝  */ @Service("httpClientManagerFactoryBen") public class HttpClientManagerFactoryBen implements FactoryBean<CloseableHttpClient>, InitializingBean, DisposableBean {    /**    * FactoryBean生成的目標對象    */   private CloseableHttpClient client;      @Autowired   private ConnectionKeepAliveStrategy connectionKeepAliveStrategy;      @Autowired   private HttpRequestRetryHandler httpRequestRetryHandler;      @Autowired   private DefaultProxyRoutePlanner proxyRoutePlanner;      @Autowired   private PoolingHttpClientConnectionManager poolHttpcConnManager;      @Autowired   private RequestConfig config;        // 銷毀上下文時,銷毀HttpClient實例   @Override   public void destroy() throws Exception {          /*       * 調用httpClient.close()會先shut down connection manager,然后再釋放該HttpClient所占用的所有資源,       * 關閉所有在使用或者空閑的connection包括底層socket。由于這里把它所使用的connection manager關閉了,       * 所以在下次還要進行http請求的時候,要重新new一個connection manager來build一個HttpClient,       * 也就是在需要關閉和新建Client的情況下,connection manager不能是單例的.       */         if(null != this.client){       this.client.close();       }   }    @Override// 初始化實例   public void afterPropertiesSet() throws Exception {          /*      * 建議此處使用HttpClients.custom的方式來創建HttpClientBuilder,而不要使用HttpClientBuilder.create()方法來創建HttpClientBuilder      * 從官方文檔可以得出,HttpClientBuilder是非線程安全的,但是HttpClients確實Immutable的,immutable 對象不僅能夠保證對象的狀態不被改變,      * 而且還可以不使用鎖機制就能被其他線程共享      */          this.client = HttpClients.custom().setConnectionManager(poolHttpcConnManager)         .setRetryHandler(httpRequestRetryHandler)         .setKeepAliveStrategy(connectionKeepAliveStrategy)         .setRoutePlanner(proxyRoutePlanner)         .setDefaultRequestConfig(config)         .build();   }      // 返回實例的類型   @Override   public CloseableHttpClient getObject() throws Exception {     return this.client;   }    @Override   public Class<?> getObjectType() {     return (this.client == null ? CloseableHttpClient.class : this.client.getClass());   }      // 構建的實例為單例   @Override   public boolean isSingleton() {     return true;   }  } 

七、增加配置文件

# 代理的host httpclient.config.proxyhost=xxx.xx.xx.xx # 代理端口 httpclient.config.proxyPort=8080 # 連接超時或異常重試次數 httpclient.config.retryTime=3 # 長連接保持時間,單位為s httpclient.config.keepAliveTime=30 # 連接池最大連接數 httpclient.config.connMaxTotal=20 httpclient.config.maxPerRoute=20 # 連接超時時間,單位ms httpclient.config.connectTimeout=2000 # 請求超時時間 httpclient.config.connectRequestTimeout=2000 # sock超時時間 httpclient.config.socketTimeout=2000 # 連接存活時間,單位s httpclient.config.timeToLive=60 

八、測試

測試代碼如下:

import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;  import javax.annotation.Resource;  import org.apache.http.Consts; import org.apache.http.ParseException; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner;  @RunWith(SpringRunner.class) @SpringBootTest public class HttpClientManagerFactoryBenTest {     // 注入HttpClient實例     @Resource(name = "httpClientManagerFactoryBen")   private CloseableHttpClient client;      @Test   public void test() throws ClientProtocolException, IOException, InterruptedException{     ExecutorService service = Executors.newFixedThreadPool(2);     for(int i=0; i<10; i++){       service.submit(new Runnable() {                  @Override         public void run() {           System.out.println("the current thread is:"+Thread.currentThread().getName());                     HttpEntity entity = null;                     try {             HttpGet get = new HttpGet("https://localhost:8080/testjson");             // 通過httpclient的execute提交 請求 ,并用CloseableHttpResponse接受返回信息             CloseableHttpResponse response = client.execute(get);             System.out.println("client object:"+client);                         entity = response.getEntity();                         System.out.println("============"+EntityUtils.toString(entity, Consts.UTF_8)+"=============");                         EntityUtils.consumeQuietly(entity);// 釋放連接                     } catch (ClientProtocolException e) {             e.printStackTrace();           } catch (ParseException e) {             e.printStackTrace();           } catch (IOException e) {             e.printStackTrace();           } finally{                         if(null != entity){// 釋放連接                 EntityUtils.consumeQuietly(entity);                }                     }                 }       });     }     Thread.sleep(60000);   } } 

通過上面的幾個步驟,就基本上完成了對HttpClient的封裝,如果需要更細致的話,可以按照上面的思路,逐步完善,將HttpClient封裝成HttpClientTemplate,因為CloseableHttpClient內部使用了回調機制,和JdbcTemplate,或者是RedisTemplate類似,直到可以以spring boot starter的方式提供服務。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
黑人狂躁日本妞一区二区三区| 日本在线精品视频| 国产精品第七十二页| 欧美成人精品在线播放| 亚洲精品成人免费| 成人在线视频福利| 成人a在线观看| 国产不卡av在线免费观看| 狠狠爱在线视频一区| 亚洲欧美日韩国产精品| 亚洲午夜未满十八勿入免费观看全集| 国产日韩欧美中文| 91久久国产精品91久久性色| 精品呦交小u女在线| 最近中文字幕mv在线一区二区三区四区| 日韩av中文字幕在线免费观看| 最好看的2019的中文字幕视频| 91精品视频观看| 精品成人在线视频| 久久久国产精品免费| 亚洲综合在线做性| 国产精品久久久久久久久免费| 亚洲免费av电影| 亚洲精品国产精品国自产观看浪潮| 亚洲欧洲美洲在线综合| 韩国v欧美v日本v亚洲| 另类色图亚洲色图| 日韩成人中文字幕在线观看| 日韩电影免费观看在线| 欧美一级大片在线免费观看| 国产69精品久久久久9999| 日韩精品福利在线| 久久亚洲精品毛片| 亚洲社区在线观看| 久久久国产一区二区| 欧美超级免费视 在线| 成人午夜一级二级三级| 日韩免费不卡av| 亚洲第一在线视频| 亚洲另类图片色| 欧美日韩综合视频| 欧美大片免费观看在线观看网站推荐| 91最新国产视频| 亚洲国产精品va在线看黑人| 国产日韩在线亚洲字幕中文| 国产福利精品av综合导导航| 国模精品一区二区三区色天香| 久久久久成人网| 国产精品欧美一区二区三区奶水| 成人免费视频xnxx.com| 亚洲综合av影视| 久久久噜噜噜久久| 亚洲激情中文字幕| 国产精品美女久久久久av超清| 成人国内精品久久久久一区| 欧美成人全部免费| 91精品国产综合久久久久久久久| 欧美午夜丰满在线18影院| 久久天堂电影网| 国产专区精品视频| 欧美黑人xxxx| 国产精品扒开腿做爽爽爽的视频| 97精品国产97久久久久久免费| 欧美日韩在线免费| 91精品国产色综合久久不卡98| 亚洲精品国产精品自产a区红杏吧| 国产a∨精品一区二区三区不卡| 日韩的一区二区| 国产免费一区视频观看免费| 亚洲福利视频网站| 欧美日韩国产中文精品字幕自在自线| 97国产精品免费视频| 国产亚洲精品美女久久久久| 精品国产成人av| 精品国产区一区二区三区在线观看| 中文字幕日韩欧美精品在线观看| 奇米成人av国产一区二区三区| 一区二区三区美女xx视频| 国产精品久久久av| 欧美性猛交xxxx富婆弯腰| 久久精品中文字幕电影| 亚洲成av人乱码色午夜| 久久午夜a级毛片| 国产精品美女久久久免费| 日韩电影免费观看中文字幕| 欧美另类极品videosbestfree| 色悠久久久久综合先锋影音下载| 久久国内精品一国内精品| 欧美成人免费在线视频| 久久久精品久久| 中文字幕自拍vr一区二区三区| 国产精品久久电影观看| 国产精品一久久香蕉国产线看观看| 精品久久香蕉国产线看观看gif| 日韩免费在线电影| 91av视频在线观看| 成人福利在线视频| 久久综合久久美利坚合众国| 91中文字幕在线观看| 成人午夜激情网| 91亚洲精品视频| 久久久亚洲网站| 欧美www视频在线观看| 欧美老女人bb| 538国产精品一区二区免费视频| 亚洲欧美资源在线| 亚洲视频欧洲视频| 欧美精品电影免费在线观看| 5252色成人免费视频| 欧美一级淫片丝袜脚交| 18久久久久久| 国产精品久久久久久久久久免费| 国产精品丝袜久久久久久高清| 欧美色视频日本版| 揄拍成人国产精品视频| 91av在线免费观看| 欧美亚洲另类制服自拍| 日韩精品一区二区视频| 精品视频中文字幕| 精品亚洲一区二区| 亚洲第一色在线| 亚洲第一精品福利| 91黑丝在线观看| 亚洲va久久久噜噜噜久久天堂| 91精品国产色综合| 日韩福利在线播放| 91九色国产社区在线观看| 久久久久久久国产精品| 午夜美女久久久久爽久久| 久久久久国产精品www| 精品少妇一区二区30p| 日韩精品在线视频美女| 97精品久久久中文字幕免费| 亚洲精品在线视频| 日韩欧美国产中文字幕| 4444欧美成人kkkk| 欧美性色19p| 九九综合九九综合| 日本中文字幕成人| 亚洲人成欧美中文字幕| 成人av色在线观看| 久久久精品999| 日韩欧美第一页| 精品国产视频在线| 欧美在线观看日本一区| 黑人巨大精品欧美一区二区免费| 九九久久久久久久久激情| 亚洲japanese制服美女| 亚洲一区二区三区xxx视频| 中文字幕日韩综合av| 久久久久国产视频| 久热精品在线视频| 69影院欧美专区视频| 久久久久久久久综合| 91国偷自产一区二区三区的观看方式| 国产欧美 在线欧美| 亚洲xxx自由成熟| 97婷婷大伊香蕉精品视频| 91精品国产91久久久久| 亚洲第一视频在线观看| 亚洲最大成人在线| 日本高清不卡在线| 亚洲欧美日韩区| 精品成人乱色一区二区|