当前位置: 首页 > article >正文

HttpClient通讯时间过久

问题背景

数据枢纽调用第三方服务时,同一个服务有时候耗时短,有时候耗时长,故做排查
系统配置:
* HttpClient 单路由的最大连接数200、总最大连接数1000
* HttpClient 获取连接请求超时时间(从连接池中获取连接的时间)1000ms、连接超时时间(与数据源请求建立连接的时间)250ms、SocketTimeout(读取超时时间)1000ms
问题现象:

  • 监控告警超时时间达到了2000ms,那我们其实预期的是1000ms超时的。

针对项目中HTTP客户端工具类调用第三方服务耗时较长的问题,以下是逐步排查方案:


1. 确认连接池问题

检查连接池配置
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(1000);       // 总最大连接数
connManager.setDefaultMaxPerRoute(200); // 每个路由(目标主机)的最大连接数
  • 现象:若MaxTotalDefaultMaxPerRoute设置过低,高并发时会出现等待连接。
  • 排查方法
    • 添加连接池监控日志:
      HttpClientContext context = HttpClientContext.create();
      try (CloseableHttpResponse response = httpClient.execute(request, context)) {
          HttpConnectionMetrics metrics = context.getConnection().getMetrics();
          System.out.println("连接请求耗时: " + metrics.getRequestCount());
      }
      
    • 监控连接池状态:
      connManager.getTotalStats().getAvailable(); // 可用连接数
      connManager.getTotalStats().getLeased();    // 被租用的连接数
      
启用连接池警告日志

在log4j或logback中配置:

<!-- HttpClient 连接池日志 -->
<Logger name="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" level="DEBUG"/>
  • 当日志中出现Connection request timeoutTimeout waiting for connection时,说明连接池不足。

2. 分析各阶段耗时(代码级监控)

添加自定义请求拦截器
httpClient = HttpClients.custom()
        .addInterceptorFirst(new HttpRequestInterceptor() {
            @Override
            public void process(HttpRequest request, HttpContext context) {
                context.setAttribute("startTime", System.currentTimeMillis());
            }
        })
        .addInterceptorLast(new HttpResponseInterceptor() {
            @Override
            public void process(HttpResponse response, HttpContext context) {
                long startTime = (Long) context.getAttribute("startTime");
                long dnsTime = (Long) context.getAttribute("dnsTime"); // 需要结合其他拦截器
                long tcpHandshakeTime = ... ; 
                System.out.println("总耗时: " + (System.currentTimeMillis() - startTime));
            }
        })
        .build();
使用Apache HttpClient内置计时

启用详细日志:

<!-- 启用Wire日志(原始网络数据,慎用) -->
<Logger name="org.apache.http.wire" level="DEBUG"/>
<!-- 请求执行时间日志 -->
<Logger name="org.apache.http.impl.execchain" level="TRACE"/>

日志会输出类似:

DEBUG org.apache.http.impl.execchain.MainClientExec - Connection request: 2000 ms
DEBUG org.apache.http.impl.execchain.MainClientExec - Connection established: 150 ms
DEBUG org.apache.http.impl.execchain.MainClientExec - Request sent: 50 ms

3. 网络层抓包分析

使用tcpdump/Wireshark
# 抓取所有进出eth0网卡、目标端口为80或443的流量
tcpdump -i eth0 -w http_capture.pcap 'tcp port 80 or tcp port 443'
  • 分析重点
    • TCP三次握手:SYN到SYN-ACK的时间(判断网络延迟)。
    • SSL/TLS握手:ClientHello到ServerHelloDone的时间(若使用HTTPS)。
    • HTTP请求/响应时间:最后一个请求包到第一个响应包的时间(服务端处理时间)。
使用tcptraceroute检测路由
tcptraceroute -p 443 api.target.com
  • 检测是否存在异常路由节点。

4. 服务端处理时间验证

手动发送测试请求
# 使用curl带时间统计
curl -w '
DNS解析: %{time_namelookup}s\n
TCP连接: %{time_connect}s\n
SSL握手: %{time_appconnect}s\n
请求开始到响应首包: %{time_starttransfer}s\n
总时间: %{time_total}s\n' \
-o /dev/null -s 'https://api.target.com/endpoint'
  • 对比工具类中的耗时,确认问题是否在客户端。

5. 高级工具诊断

使用JProfiler/YourKit分析
  • 重点监控:
    • PoolingHttpClientConnectionManagerleaseConnection方法耗时。
    • SocketInputStream.readSocketOutputStream.write的阻塞时间。
启用JFR(Java Flight Recorder)
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder ...
  • 监控HttpClient相关方法调用栈及耗时。

6. 针对性优化方案

连接池不足
  • 调整MaxTotalDefaultMaxPerRoute,根据压测结果动态设置。
DNS延迟
  • 启用客户端DNS缓存:
    System.setProperty("networkaddress.cache.ttl", "60"); // 设置DNS缓存时间(秒)
    
SSL握手慢
  • 使用更快的加密算法(如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)。
  • 预加载SSL上下文:
    SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(...).build();
    sslContext.createSSLEngine().getSupportedCipherSuites(); // 提前初始化
    

总结排查步骤

  1. 检查连接池日志 → 确认是否因连接池满导致等待。
  2. 添加拦截器或启用TRACE日志 → 定位耗时在连接获取、发送还是读取阶段。
  3. 使用curl对比测试 → 确认是否为服务端问题。
  4. 抓包分析TCP/SSL时间 → 定位网络层问题。
  5. 调整参数复测 → 验证优化效果。

通过以上步骤,可精准定位是连接池瓶颈、网络延迟还是服务端处理慢导致的问题。


http://www.kler.cn/a/593671.html

相关文章:

  • MiniMax GenAI 可观测性分析:基于阿里云 SelectDB 构建 PB 级别日志系统
  • python采集小红书笔记详情API接口,json数据示例分享
  • 理工超市-多用户注册
  • 【C++模板】
  • Linux--进程创建
  • Android 13深度定制:SystemUI状态栏时间居中显示终极实战指南
  • The Illustrated Stable Diffusion
  • 电机控制常见面试问题(十四)
  • pytorch v1.4.0安装问题
  • 2024年国赛高教杯数学建模E题交通流量管控解题全过程文档及程序
  • 嵌入式系统开发如何选择和备考软考高级
  • webpack等构建工具如何支持移除未使用的代码
  • 基于carla的模仿学习(附数据集CORL2017)更新中........
  • WPF 中的 GridSplitter 详解
  • 不使用负压电源,ADC如何测量正负压?
  • 为什么渲染农场渲染帧而非视频?核心原因 + 举例
  • Neo4j GDS-02-graph-data-science 简单聊一聊图数据科学插件库
  • 计算机网络基础:设计高效的网络布局
  • 使用cartographer扩展地图
  • 【Linux】VMware 17 安装 VMware Tools