浅谈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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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。…