日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java httpclient 关闭_【Java系列007】HttpClient调用:你考虑过关闭连接、并发了吗?...

發布時間:2023/12/10 java 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java httpclient 关闭_【Java系列007】HttpClient调用:你考虑过关闭连接、并发了吗?... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

你好!我是miniluo,今天和你分享使用HttpClient過程中,未考慮釋放連接和并發導致的坑。

HttpClient在項目中還是比較常見的,主要都是通過GET或POST請求第三方以獲取響應結果。前段時間還了解到也有企業用它來做爬蟲。下面我們就從兩方面來一起學習HttpClient。

強占著不放

我們先來看一張因未釋放連接導致的異常圖。

我們再來看代碼,代碼很簡單,用一個static修飾HttpClient的一個對象,也是用static的方式實例化(并發下,static實例化的對象是線程不安全的)。

1@Slf4j

2public?class?MyHttpClientTest{

3????private?static?HttpClient?client;

4????static?{

5????????RequestConfig?requestConfig?=?RequestConfig.custom()

6????????????????.setConnectTimeout(5000)

7????????????????.setConnectionRequestTimeout(3000)

8????????????????.setSocketTimeout(5000).build();

9????????client?=?HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)

10????????????????.build();

11????}

12

13????@Test

14????public?void?testHttpClient(){

15????????for?(int?i?=?0;?i?

16????????????String?url?=?"http://127.0.0.1:9092/learn/product/get/12";

17????????????HttpGet?httpGet?=?new?HttpGet(url);

18????????????try?{

19????????????????HttpResponse?res?=?client.execute(httpGet);

20????????????}?catch?(IOException?e)?{

21????????????????log.error("異常:?",e);

22????????????????return;

23????????????}

24????????}

25????}

26}

我們看回異常圖,圖中有2個紅框,我們先來看第一個紅框的“[total kept alive: 0; route allocated: 2 of2; total allocated: 2 of 20]”,這個DEBUG級別日志描述的什么意思呢?也就是說這個route下共有2個連接,已用2個;pool共有20個,已用2個。再來看第二個紅框“Timeout waiting for connection from pool”,從連接池獲取連接等待超時。奇怪吧,這順序執行也會出現?這其實是前兩個連接響應結果回來后,并沒有釋放連接資源,導致后面的請求等待超時。

我們看看PoolingHttpClientConnectionManager類的構造函數,其給Pool初始化時給maxConnPerRoute和maxConnTotal設置了默認值。

1????public?PoolingHttpClientConnectionManager(

2????????final?HttpClientConnectionOperator?httpClientConnectionOperator,

3????????final?HttpConnectionFactory?connFactory,

4????????final?long?timeToLive,?final?TimeUnit?tunit){

5????????super();

6????????this.configData?=?new?ConfigData();

7????????//defaultMaxPerRoute默認為2,maxTotal默認為20

8????????this.pool?=?new?CPool(new?InternalConnectionFactory(

9????????????????this.configData,?connFactory),?2,?20,?timeToLive,?tunit);

10????????this.pool.setValidateAfterInactivity(2000);

11????????this.connectionOperator?=?Args.notNull(httpClientConnectionOperator,?"HttpClientConnectionOperator");

12????????this.isShutDown?=?new?AtomicBoolean(false);

13????}

所以當請求連接被占用2個后,后面的請求并不會獲取到連接,這么說,我們需要釋放連接資源。這里我們需要介紹EntityUtils這個類,里面包含了兩個釋放連接資源的方法consumeQuietly(HttpEntity entity)和consume(HttpEntity entity),前者內部也是請求后者完成關閉InputStream。調整后的代碼如下:

1@Test

2????public?void?testHttpClient(){

3????????for?(int?i?=?0;?i?

4????????????String?url?=?"http://127.0.0.1:9092/learn/product/get/12";

5????????????HttpGet?httpGet?=?new?HttpGet(url);

6????????????HttpResponse?res?=?null;

7????????????try?{

8????????????????res?=?client.execute(httpGet);

9????????????}?catch?(IOException?e)?{

10????????????????log.error("異常:?",?e);

11????????????????return;

12????????????}?finally?{

13????????????????if?(null?!=?res)?{

14????????????????????EntityUtils.consumeQuietly(res.getEntity());

15????????????????}

16????????????}

17????????}

18????}

調整完代碼后,我們執行后發現5個請求都能正確請求并得到響應結果。或許有同學會問到如果我調整maxConnPerRoute和maxConnTotal不也行嗎?可以,但是你的連接還是沒有釋放,當超過你設置值后也會出現無法獲得連接池的問題。

被CloseableHttpClient名字所坑

最近項目中就是使用到CloseableHttpClient實現請求第三方,踩完上面的坑后,我們在項目中寫了一個工廠類內部用枚舉方式實現單例。經過并發測試后,的確沒有出現上述坑,看了CloseableHttpClient有一個execute方法,最后也有釋放資源,所以沒有在意。直到今天寫文章對應的測試案例我才發現我被這個方法欺騙了。

CloseableHttpClient的execute()重載了多個方法,而實際調用能釋放資源的execute必須是包含ResponseHandler extends T>這個屬性,否則和原有HttpClient方式是一樣的。為何,模擬高并發下并沒有發生問題呢?這是因為項目中請求響應結果后是用下面的代碼完成數據獲取的。

1?HttpResponse?response?=?client.execute(get);

2?String?res?=?EntityUtils.toString(response.getEntity());

跟進EntityUtils.toString()方法的最底層調用,會執行InputStream關閉流,和上文說的consume方法一樣。但是這里坑就坑在,如果請求第三方超時或其他異常,則直接跳過,并沒有執行上面的toString()方法,那就不會釋放連接資源。

知道坑所在,要解決也就好辦。可以有下述兩個方案:

1、外層調用增加finally釋放連接,上文所述。

2、實例化一個ResponseHandler對象,調用實現了釋放連接資源的方法。

總結

今天我們一起學習了HttpClient和CloseableHttpClient沒有正確釋放連接,以及并發受限的問題。實際項目中如果沒有考慮到這兩個情況,則會帶來生產問題,后面的請求一直無法獲取到連接資源,以及并發量起不來。很多項目并不會有這個問題是因為HttpClient和CloseableHttpClient并不是單例的,每次使用都會是新的實例。希望通過今天的學習對你日后使用HttpClient更加得心應手。由于篇幅問題,沒有把CloseableHttpClient案例放上來,有興趣的同學可以到GitHub上下載(https://github.com/littleluo/bj-share-java.git)。

思考和討論

1、案例中,我們使用了static和枚舉的方式解決單例問題,除了這兩種方法外,還有哪些方式呢?他們各自的優缺點是什么?

2、說回前面關于分布式鎖的文章中,我們用到了redis,也提到client.close()方式歸還連接,為何是歸還連接,而不是關閉連接呢?

歡迎留言與我分享和指正!也歡迎你把這篇文章分享給你的朋友或同事,一起交流。

感謝您的閱讀,我們下節再見!

掃碼關注我們,與君共進

總結

以上是生活随笔為你收集整理的java httpclient 关闭_【Java系列007】HttpClient调用:你考虑过关闭连接、并发了吗?...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。