You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tomcat最大线程数无法突破300,高负载下CPU利用率不足求助

低CPU利用率但微服务响应超时排查方案

核心问题分析

CPU利用率低但响应时长暴增,说明服务线程大部分处于等待状态而非执行计算逻辑,问题大概率出在远程调用依赖的下游服务/网络,或本地线程池、连接池的配置未生效。

具体排查步骤

1. 验证下游服务的可用性与性能

  • 直接压测下游服务,查看其在1000并发下的响应时间、错误率、CPU/内存负载。如果下游服务本身响应超时(超过你设置的2秒),或连接数耗尽,会导致本地线程阻塞等待,CPU自然空闲。
  • 检查下游服务日志,确认是否存在大量超时、拒绝连接或慢查询记录。

2. 确认RestTemplate的超时与连接池配置是否生效

默认RestTemplate不自带连接池,必须手动配置HttpComponentsClientHttpRequestFactory才能启用连接池,否则每次请求会新建HTTP连接,极易耗尽系统句柄导致阻塞:

// 正确的连接池配置示例
@Bean
public RestTemplate restTemplate() {
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    // 匹配业务要求的2秒超时
    factory.setConnectTimeout(2000);
    factory.setReadTimeout(2000);
    
    // 配置HttpClient连接池
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
    connectionManager.setMaxTotal(200); // 对应Tomcat max-threads
    connectionManager.setDefaultMaxPerRoute(200); // 同一路由的最大连接数
    factory.setHttpClient(HttpClients.custom().setConnectionManager(connectionManager).build());
    
    return new RestTemplate(factory);
}
  • 用netstat或ss命令查看本地与下游服务的连接数,是否达到配置的maxConnTotal上限,是否出现TIME_WAIT或CLOSE_WAIT状态的连接堆积。

3. 检查远程调用线程池的使用逻辑

  • 如果每个请求都新建ForkJoinPool(比如new ForkJoinPool(6)),会导致系统线程数爆炸,最终被操作系统限制,反而无法并行执行。应改用全局共享的线程池:
    // 全局共享的ForkJoinPool,并行度根据CPU核心数或业务需求设置
    private static final ForkJoinPool REMOTE_CALL_POOL = new ForkJoinPool(16);
    
    // 发起并行调用时使用共享池
    List<CompletableFuture<String>> futures = requests.stream()
        .map(req -> CompletableFuture.supplyAsync(() -> remoteCall(req), REMOTE_CALL_POOL))
        .collect(Collectors.toList());
    
  • 用VisualVM或jstack查看线程状态,确认远程调用线程是否大部分处于WAITING (parking)状态(等待下游响应),还是被线程池阻塞。

4. 验证Tomcat线程池的实际负载

  • 查看Spring Boot Actuator的/actuator/metrics/tomcat.threads端点,确认tomcat.threads.active是否达到max-threads=200的上限。如果活跃线程数远低于200,说明请求在Tomcat的连接队列(accept-count)中堆积,原因可能是:
    • 下游调用阻塞了Tomcat工作线程,导致线程无法释放处理新请求;
    • 缺少请求处理超时配置(server.connection-timeout=3000是连接建立超时,不是请求处理超时),需补充:
      server.tomcat.connection-timeout=3000
      server.tomcat.async-timeout=10000 # 异步请求超时(若使用异步处理)
      

5. 确认超时配置是否真正生效

  • 远程调用的2秒超时必须同时设置连接超时和读取超时,如果只设置其中一个,线程可能无限等待。比如RestTemplate的readTimeout未设置,即使下游服务无响应,线程也会一直阻塞。
  • 临时调低超时时间(比如1秒),观察响应时长是否缩短、CPU利用率是否变化,以此验证超时是否生效。

总结

优先排查下游服务的性能瓶颈,再验证本地连接池、线程池的配置是否正确生效。低CPU+慢响应的典型场景是线程阻塞等待外部资源,而非本地计算能力不足,不要盲目调大线程数,反而会加剧资源竞争。

内容的提问来源于stack exchange,提问作者Manas Saxena

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 00:15:39