20230920-Apache httpclient工具分享

一、工具简介

HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

二、工具结构

HttpClient使用了责任链模式,所有Executor都实现了ClientExecChain接口的execute()方法

2.1 执行链介绍

2.2 连接池程池交互流程

2.3 连接的等待与唤醒

唤醒连接:当有连接释放时就会触发唤醒

  • httpclient做等待和唤醒使用的ReentrantLock且配置为是非公平锁
  • 放入等待队列时调用condition.await()/awaitUntil(deadline)方法
  • 唤醒时调用condition.signalAll()方法

三、参数配置

1.HttpClients参数配置

1.1 配置连接策略(KeepAliveStrategy)

默认连接策略DefaultConnectionKeepAliveStrategy.INSTANCE

默认的连接策略返回header中的Keep-Alive:timeout乘以1000。下图为连接保持60秒

如果没有返回则是走默认时长(ConnectionManager中的timeToLive配置参数)

MainClientExec会设置过期时间

HttpClientBuilder初始化会有空闲连接池过期策略处理(IdleConnectionEvictor),此策略单独启线程循环执行检查过期连接并关闭。

1.2 配置连接管理类(ConnectionManager)

ConnectionManager有哪些?

BasicHttpClientConnectionManager是一次只能有一个线程使用的单个连接

PoolingHttpClientConnectionManager支持多个连接,连接交由连接池管理。(默认配置)

1.3 开启httpClient过期连接清理处理类(IdleConnectionEvictor)

evictExpiredConnections : 开启

evictIdleConnections:

maxIdleTime:长连接在空闲连接池中的保留时间。此值配置后除了链接本身的过期时间到了会清理,链接闲置超出此配置的时间到了也会被清理,对连接策略有一定冲突。

maxIdleTimeUnit:单位

1.4 重试策略(RetryHandler)

默认策略为DefaultHttpRequestRetryHandler.INSTANCE

disableAutomaticRetries()关闭重试策略

setRetryHandler(HttpRequestRetryHandler)设置自己的重试逻辑

1.5 设置默认请求配置(RequestConfig)

1.6 设置拦截器addInterceptorFirst\addInterceptorLast

LinkedList<HttpRequestInterceptor>:requestFirst、requestLast

LinkedList<HttpResponseInterceptor>:responseFirst、responseLast

可对请求及响应设置拦截器

执行顺序:requestFirst/responseFirst -> httpClient默认拦截器 -> requestLast/responseLast

2.PoolingHttpClientConnectionManager参数配置

构造参数:

  • Registry<ConnectionSocketFactory> :协议注册,通常情况注册http、https协议。需要注意默认的https请求对安全证书有要求,如果系统安全需求不高的情况可以对所有ssl信任。
  • HttpConnectionFactory:提供管理和创建HTTP服务器的连接方式,默认ManagedHttpClientConnectionFactory。无特殊需求无需更改
  • SchemePortResolver:根据请求地址获取端口,默认DefaultSchemePortResolver可根据协议返回响应端口。无特殊需求无需更改
  • DnsResolver:将主机名解析成ip地址,方便建立连接。
  • timeToLive/timeUnit:连接存活时间(连接策略优先于此配置)

其它设置:

  • MaxTotal:总连接池大小。当并发请求数大于设置值时,放入等待队列中。
  • DefaultMaxPerRoute:默认单地址连接池大小,当某地址(如http://www.abc.com)并发请求数大于设置值时,放入等待队列
  • MaxPerRoute:与DefaultMaxPerRoute作用一致,对某个连接的池子数进行单独配置

3.设置默认请求配置(RequestConfig)

常用:

  • ConnectTimeout:请求超时时间
  • SocketTimeout:响应超时时间
  • ConnectionRequestTimeout:连接池等待时间
  • setExpectContinueEnabled:客户端会先发送一个带有"Expect: 100-continue"头部的请求建立握手,可以避免请求失败却发送大量信息

不常用:

  • setRedirectsEnabled:是否允许HTTP请求自动处理重定向
  • setLocalAddress:指定本地请求网络。(InetAddress.getByAddress(new byte[]{192, 168, 0, 100}))
  • setCookieSpec:Cookie策略。默认为CookieSpecs.DEFAULT
  • setCircularRedirectsAllowed:无限重定向开关,默认关闭,谨慎打开
  • setMaxRedirects:无限重定向次数
  • setAuthenticationEnabled:是否启用HTTP身份验证功能
  • setContentCompressionEnabled:内容压缩,可以根据请求头中的Accept-Encoding字段来决定是否启用内容压缩。
  • setNormalizeUri:uri规范化。示例:http://abc.com/../xxx会改为http://abc.com/xxx

 四、httpClient耗时问题及分析

1.问题现象

httpClient请求耗时从日志时间看超过10分钟

2.排查流程

  • 通常情况请求耗时过高一般是因为:
    • 请求超时(也就是三次握手)慢或不可用 - ConnectTimeout
    • 响应超时与服务器建立连接但因网络或服务端响应慢导致超时 - SocketTimeout
    • 大报文耗时:受制于网络传输时间、带宽等条件限制,此情景不会触发超时异常。
  • 上述情况可以分别对请求、响应超时时间进行配置来解决问题。实际项目情况已经配置过超时时间。
  • 为检验配置是否生效对http工具进行上述两种超时场景进行模拟
    • 请求超时:采用链接一个不可到达的IP地址http://10.253.64.2:8080/test
    • 响应超时:服务端睡眠时间>httpClient工具配置响应超时时间
  • 经检验配置生效,均在规定时间内抛出超时异常
  • 配置正常未检测出耗时长的问题超出认知范围
  • 回顾场景,通过图查看问题时间段的并发现象。下图为应用一天顶峰时tps\tp90\tp999

  • 根据图可知单机房最高并发接近90,m6机房3个机器,单机器并发30,tp90:5-15秒之间,tp999则不稳定最高可到10分
  • 猜测高并发+高耗时可能是导致发生超长耗时的原因
  • 开始模拟场景
    • 每秒30并发
    • 服务端响应时间全部为10秒才响应
    • 总连接最大连接数为30(实际项目设置的是150)
    • 单地址连接最大连接数为30(实际项目设置的是150)
  • 压测后的结果出现先请求的应用却后响应的现象。下图是当请求+响应耗时极限时间20秒时,却出现40秒响应的现象(发送body内容很少不可能是因为信息传输增加耗时)

  • 经排查源码发现当连接数打满时会放入等待队列中,等待队列当没设置超时时间则会一直等待。
  • 等待队列释放条件当有请求释放连接时,对列头部的连接则会释放去抢夺连接。
  • 开始极限压测,问题暴露就更加明显

  • 排查源码MultiThreadedHttpConnectionManager.doGetConnection。common包httpClient获取连接流程​​​​​​
  • 值得一提的是这里的等待用的是Object.wait,唤醒是中断线程实现(waitingThread.thread.interrupt())

  • 至此问题原因定位90%

3.问题解决

  1. 尽量避免连接池打满:增加单连接、总连接池的连接数,避免触碰极限值。注意:系统承受能力、下游承受能力
  2. 对等待连接池时间进行控制:配置连接池等待时间

效果:

tp90:

tp:999

  • 至此问题原因定位100%

五、httpClient压测Demo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作为一个AI语言模型,我无法提供wechatpay-apache-httpclient的完整用例,但我可以为您提供一些参考资料和建议: 1. 我们可以在官方文档中找到wechatpay-apache-httpclient的使用方法。可以参考https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/httpclient-ru-he-shi-yong 2. 首先,需要在项目中引入wechatpay-apache-httpclient的依赖,例如在Maven项目中,可以在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.wechatpay</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>6.5.6</version> </dependency> ``` 3. 接下来,我们需要配置httpClient,包括设置连接池、连接超时时间、读取超时时间等。例如: ```java PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(100); // 最大连接数 connectionManager.setDefaultMaxPerRoute(20); // 单个路由最大连接数 RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(5000) // 连接超时时间 .setSocketTimeout(5000) // 读取超时时间 .build(); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .build(); ``` 4. 然后,我们可以使用httpClient发送请求,并处理响应结果。例如: ```java HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"); httpPost.setHeader("Content-Type", "application/json"); httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Wechatpay-Serial", "XXXXXXXXXXX"); httpPost.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + accessToken); StringEntity entity = new StringEntity(requestJson, "UTF-8"); httpPost.setEntity(entity); HttpResponse httpResponse = httpClient.execute(httpPost); int statusCode = httpResponse.getStatusLine().getStatusCode(); String responseJson = EntityUtils.toString(httpResponse.getEntity(), "UTF-8"); if (statusCode == HttpStatus.SC_OK) { // 处理成功响应结果 } else { // 处理失败响应结果 } ``` 以上代码仅为示例,具体使用方法还需根据实际情况进行调整。同时,为了保证代码的可读性和可维护性,建议使用封装好的工具类来处理httpClient请求和响应结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值