淺談Apache HttpClient的相關配置和使用

Apache HttpClient是由Apache軟件基金會維護的一款開源HTTP客戶端庫,對比最基礎的 HttpURLConnection 而言,它的優勢時支持連接池管理,攔截器(Interceptor)機制,同步/異步請求支持等能力。

在使用這個組件時,需要格外注意連接池相關的配置,否則容易踩坑。

踩坑案例

問題

一個對外轉發請求的項目,部分渠道的對接使用了HttpClient來實現的,由于業務訪問量不大,上線后只部署了幾臺服務,前段時間三方平臺曝光量增加,導致業務量比平時多了一倍,隨后這個服務出現了問題:

從APM監控上看,上游調用該服務的請求有大量的超時,但是該服務只是請求轉發而已,從http組件監控看該服務調三方接口的請求的RT也有明顯增大,還有一部分請求出現了超時。但是該服務的CPU JVM資源指標都比較正常,而且項目中有多個平臺的對接業務,目前只有這個平臺的請求是有問題的。

排查過程

起初懷疑是網絡抖動,但是找運維看了說網絡延遲是正常的沒有抖動,即便如此,還是覺得是網絡不好導致請求hold住了(從apm看請求超時報錯時間都比較久應該是配置的不太合理),順著思路想著先擴容試試吧,擴容后發現起初是有效果的,但是過了一會兒又開始出現超時的請求了。

查到這感覺不像是網絡原因了,只能翻代碼了,隨后翻了一下代碼現狀和關于HttpClient的連接池配置資料,找到了問題的原因......

問題1:HttpClient的連接池只設置了全局最大連接MaxTotal,但是未設置單路由的最大連接defaultMaxPerRoute(默認只有2)。在并發情況下,同路由下的沒有空閑連接就會導致一直阻塞等待,直到獲取到連接才能進行請求,所以超時的請求其實是在等待獲取連接,并不是等待三方響應超時。


問題2:只有這個渠道的對接用了HttpClient,其他渠道直接用RestTemplate實現的。這就可以解釋為什么只有這個平臺的請求是有問題了
?

下面整理一下httpClient相關的配置,避免以后踩坑。

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
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.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import tech.yummy.common.caja.tools.utils.BizThreadPoolUtils;import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;@Slf4j
public class HttpClientUtils {private static  CloseableHttpClient httpClient;private static PoolingHttpClientConnectionManager clientConnectionManager;private static SSLConnectionSocketFactory sslConnectionSocketFactory;private static SSLContextBuilder sslContextBuilder;private static String encoding;private static final String HTTP = "http";private static final String HTTPS = "https";static {try {encoding = "UTF-8";Registry<ConnectionSocketFactory> registry = initConnectionSocketFactoryRegistry();clientConnectionManager = new PoolingHttpClientConnectionManager(registry);// 創建連接池(默認 maxTotal=20, defaultMaxPerRoute=2 validateAfterInactivity=2000)clientConnectionManager = new PoolingHttpClientConnectionManager(registry);//覆蓋默認配置 全局最大連接數 500clientConnectionManager.setMaxTotal(500);//覆蓋默認配置 每路由默認連接數 50clientConnectionManager.setDefaultMaxPerRoute(50);//覆蓋默認配置 連接在池中閑置多久后需要驗證其有效性 5秒clientConnectionManager.setValidateAfterInactivity(5000);RequestConfig requestConfig = RequestConfig.custom()//從連接池獲取連接的超時時間 - 連接池滿時會阻塞等待,超時拿不到鏈接會拋     ConnectionPoolTimeoutException.setConnectionRequestTimeout(2000)//建立TCP連接的超時時間(握手).setConnectTimeout(1000)//數據傳輸的間隔超時時間.setSocketTimeout(3000).build();httpClient = HttpClientBuilder.create().useSystemProperties().setConnectionManager(clientConnectionManager).setDefaultRequestConfig(requestConfig).build();} catch (Exception e) {log.error("初始化httpclient 配置執行異常", e);}}private static Registry<ConnectionSocketFactory> initConnectionSocketFactoryRegistry() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {sslContextBuilder = new SSLContextBuilder();// 全部信任 不做身份鑒定sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {@Overridepublic boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {return true;}});sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build(),null,null,NoopHostnameVerifier.INSTANCE);Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register(HTTP, new PlainConnectionSocketFactory()).register(HTTPS, sslConnectionSocketFactory).build();return registry;}private static String request(HttpUriRequest request) throws IOException {ResponseHandler<String> responseHandler = response -> {int status = response.getStatusLine().getStatusCode();log.debug("response status:{}", status);HttpEntity entity = response.getEntity();return entity != null ? EntityUtils.toString(entity, encoding) : null;};log.debug("httpClient request:{} {}", request.getMethod(), request.getURI());String responseBody = httpClient.execute(request, responseHandler);log.debug("httpClient response:{}", responseBody);return responseBody;}//======================================================GET Start====================================================================public String get(String url, String params) throws IOException {if (!StringUtils.isBlank(params)) {url = url.concat("?").concat(params);}HttpGet httpGet = new HttpGet(url);return request(httpGet);}public static String get(String url, String params, Map<String, String> headers) throws IOException {if (!StringUtils.isBlank(params)) {url = url.concat("?").concat(params);}HttpGet httpGet = new HttpGet(url);if (headers != null) {headers.forEach(httpGet::setHeader);}return request(httpGet);}//======================================================GET End====================================================================//======================================================POST Start====================================================================/*** POST -> JSON 通用*/public static String post(String url, String rawContents, Map<String, String> headers) throws IOException {HttpPost httpPost = new HttpPost(url);HttpEntity entity = new StringEntity(rawContents, encoding);httpPost.setEntity(entity);if (headers != null) {headers.forEach(httpPost::setHeader);}return request(httpPost);}//======================================================POST End====================================================================public static void main(String[] args) throws IOException, InterruptedException {String url = "https://www.test.com";Map<String, String> headers = new HashMap<>();headers.put("Content-Type", "application/json");headers.put("charset", "UTF-8");headers.put("token", "ST-10384-3D0AQqHag-QqmKby4Upyu6YdB4f45fcc9-9qjdd");//參數String postParam = "{\"pageNum\":1,\"pageSize\":10}";for(int i = 0;i < 7;i++){String finalUrl = url;BizThreadPoolUtils.submit(() ->{long start = System.currentTimeMillis();try {String postResponse = HttpClientUtils.post(finalUrl, postParam, headers);log.info("請求耗時:{},返回值:{}",(System.currentTimeMillis() - start),postResponse);} catch (Exception e) {log.error("耗時:" + (System.currentTimeMillis() - start) + ",異常:" + e);}});}Thread.sleep(10000);String params = "page=1&size=10";String getResponse = HttpClientUtils.get(url, params, headers);System.out.println(getResponse);}}

?

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

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

相關文章

【Teensy】在ArduinoIDE中配置Teensy4.1

1.文件——首選項 在其他開發板管理器地址這里添加&#xff1a; https://www.pjrc.com/teensy/package_teensy_index.json 點擊確定&#xff01; 2.安裝Teensy(for Arduino IDE…) 按照圖中1&#xff0c;2&#xff0c;3操作&#xff01;可以選擇上一個版本&#xff08;不使用最…

企業自建云概念解讀|私有云、專有云、混合云、分布式云、企業云

隨著云計算技術逐漸成熟&#xff0c;越來越多的企業開始在本地數據中心自行搭建云平臺&#xff0c;滿足數據合規、業務性能與連續性、節約成本等多方面的需求。不過&#xff0c;面對多種多樣的自建云產品&#xff0c;不少用戶會有類似的疑問&#xff1a;自建云等于私有云嗎&…

反彈 Shell 升級為全交互終端的兩種高效方法

目錄 ?? 升級反彈 Shell 為全交互終端:兩種高效方法 ??? 方法 1:利用 Python pty.spawn 創建偽終端 ?? 操作步驟

Hyper-YOLO: When Visual Object Detection Meets Hypergraph Computation論文精讀(逐段解析)

Hyper-YOLO: When Visual Object Detection Meets Hypergraph Computation論文精讀&#xff08;逐段解析&#xff09; 論文地址&#xff1a;https://arxiv.org/abs/2408.04804 CVPR 2024 Yifan Feng, Jiangang Huang, Shaoyi Du, Senior Member, IEEE, Shihui Ying, Jun-Hai Y…

Windows 下配置多個 GitHub 賬號的 SSH Key

Windows 下配置多個 GitHub 賬號的 SSH Key 假設你有以下兩個 SSH key 文件&#xff1a; 第一個賬號&#xff1a;id_rsa&#xff08;默認&#xff09;第二個賬號&#xff1a;id_rsa_github ? 步驟&#xff1a;在 Windows 上配置多個 GitHub 賬號 SSH Key 1?? 打開 SSH 配…

技術選型:時序數據庫(三)

IoTDB vs InfluxDB vs TDengine 時序數據庫橫評對比。 從 架構設計、性能、功能、生態、適用場景 等維度&#xff0c;對三款時序數據庫進行深度對比&#xff0c;助您精準選型。 一、核心架構對比 數據庫 存儲模型 數據模型 擴展性 Apache IoTDB 分層存儲&#xff08;TsFi…

電子電路原理第十九章(非線性運算放大器電路的應用)

單片集成運算放大器價格便宜、用途廣泛且性能可靠。它們不僅可以用于線性電路,如電壓放大器、電流源和有源濾波器,而且可以用于非線性電路,如比較器、波形生成器和有源二極管電路。非線性運放電路的輸出通常與輸入信號的波形不同,這是因為運放在輸入周期的某個時間段內達到…

FPGA實現CameraLink視頻解碼轉SDI輸出,基于LVDS+GTX架構,提供2套工程源碼和技術支持

目錄 1、前言工程概述免責聲明 2、CameraLink協議理論學習3、相關方案推薦我已有的所有工程源碼總目錄----方便你快速找到自己喜歡的項目FPGA實現CameraLink視頻編解碼方案本博已有的 SDI 編解碼方案 4、工程詳細設計方案工程設計原理框圖輸入CameraLink相機LVDS視頻解碼模塊LV…

戶外人像要怎么拍 ?

前言&#xff1a; ” 接上篇&#xff0c;培養你的眼力 - 攝影構圖&#xff0c;本文是整理自《美國紐約攝影學院 攝影教材》&#xff0c;第三單元 - 第9課 - 自然光&#xff0c;課后習題及解答。“ 1. 正面光產生無深淺反差的平面感覺。 理解這題&#xff0c;首先得明白什么是…

華為云Flexus+DeepSeek征文 | 華為云 ModelArts Studio 賦能高情商AI聊天助手:用技術構建有溫度的智能對話體驗

前言 華為云 ModelArts Studio 是基于 ModelArts 構建的一站式大模型即服務平臺&#xff08;MaaS&#xff09;&#xff0c;可通過與開源 Agent 框架 Dify.AI 結合來開發對接 AI 聊天助手。 在打造 “高情商” 特性的過程中&#xff0c;華為云ModelArts Studio 的自定義提示詞…

Spring Boot屬性配置方式

一、Spring Boot屬性配置方式。 在編寫完成后端程序之前&#xff0c;可以通過yml配置文件鍵值對的方式修改配置環境&#xff0c;一旦打包完成&#xff0c;再次修改yml配置文件較為麻煩&#xff0c;此時&#xff0c;可以使用以下配置方式&#xff1a; 1.命令行參數方式 …

Webpack原理剖析與實現

1. 整體架構設計 Webpack 5 的整體架構設計包括以下幾個核心模塊: Compiler:負責整個編譯過程,從讀取配置、解析模塊、生成依賴圖,到輸出最終的打包結果,主要文件是 lib/Compiler.js 。 Compilation:代表一次編譯過程,包括所有模塊、依賴關系和編譯結果,主要文件是 li…

【Python使用】嘿馬python運維開發全體系教程第2篇:日志管理,Linux概述【附代碼文檔】

教程總體簡介&#xff1a;網絡設定 學習目標 1、手動設定 2、DHCP自動獲取 系統基本優化 一、永久關閉SELinux 1. 永久關閉 二、關閉防火墻 2. 臨時啟動關閉防火墻 三、設定運行級別為3&#xff08;命令行模式&#xff09; 四、修改ssh端口號 ssh服務 一、ssh介紹 二、客戶端遠…

Hibernate報No Dialect mapping for JDBC type 1111(APP)

文章目錄 環境癥狀問題原因解決方案報錯編碼 環境 系統平臺&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 癥狀 客戶應用中報錯No Dialect mapping for JDBC type 1111。 問題原因 客戶使用Hibernate&#xff0c;實體類的中設置的數據類型與數…

【數據分析】環境數據降維與聚類分析教程:從PCA到可視化

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹教程內容數據預處理主成分分析(PCA)聚類分析可視化分析結果提取簇特征教程目的加載R包數據下載導入數據數據預處理主成分分析(PCA)計算相關矩陣繪制相關矩陣熱圖執行PCA可視化…

mac 安裝python,切換python版本

一、安裝多版本的PYTHON 在macOS上&#xff0c;你可以通過Homebrew包管理器安裝多個版本的Python 安裝Homebrew 首先&#xff0c;如果你的macOS上沒有安裝Homebrew&#xff0c;需要先進行安裝。打開終端&#xff08;Terminal&#xff09;并輸入以下命令&#xff1a; /bin/b…

AMD圖形和計算架構:RNDA

AMD圖形和計算架構&#xff1a;RNDA AMD RDNA 是 AMD 為顯卡&#xff08;GPU&#xff09;設計的 圖形和計算架構&#xff0c;專為高性能游戲、實時渲染和并行計算優化。目前已經迭代到 RDNA 3&#xff08;如 RX 7000 系列顯卡&#xff09;&#xff0c;與 NVIDIA 的 RTX 系列和…

ubuntu20.04配置go環境

下載go語言安裝包 下載地址&#xff1a; All releases - The Go Programming Language 解壓安裝包 tar xfz go1.17.linux-amd64.tar.gz -C /usr/local 配置環境變量 編輯配置文件 vim ~/.bashrc # go env export GOROOT/usr/local/go export GOPATH$HOME/software/go ex…

Monorepo+Pnpm+Turborepo

以下是關于 Monorepo pnpm Workspace Turborepo 的詳細解析&#xff0c;涵蓋核心概念、技術優勢、配置實踐及協作機制&#xff0c;結合行業最佳實踐總結。 一、Monorepo 的核心價值 Monorepo&#xff08;單一倉庫&#xff09;指多個項目/模塊共享同一代碼庫的管理模式&#…

【Springai】 2指定模型的三種方式(Ollama)

Springai 指定模型的三種方式&#xff08;Ollama&#xff09; 在實際開發中&#xff0c;Ollama 支持三種常用的模型指定方式&#xff1a; 1. 從 yml 配置讀取默認模型 注意&#xff1a; 這是最基礎、最推薦的方式&#xff0c;必須先配置好才能用自動注入的 OllamaChatModel。…