努力
加油

ECS连接数达到上限但CPU空闲,可能存在什么瓶颈?

这种情况在运维排查中非常经典,通常被称为“假死”或“资源错配”。当 CPU 和内存都还有大量空闲,但连接数(Connections)却打满导致新请求无法建立时,问题往往不在计算能力上,而是在系统资源限制、网络栈配置或中间件瓶颈这几个维度。

以下是按可能性从高到低的排查思路和具体原因分析:

1. 文件描述符(File Descriptors, FD)耗尽

这是最常见的原因。在 Linux 系统中,每一个网络连接(Socket)本质上都是一个打开的文件。如果进程能打开的最大文件数有限制,一旦达到上限,即使 CPU 再闲,也无法创建新的 Socket 连接。

  • 现象:日志中可能出现 Too many open files 错误。
  • 排查方法:
    • 查看当前用户或进程的限制:ulimit -n
    • 查看当前已打开的文件句柄数:lsof -p <pid> | wc -l 或 cat /proc/<pid>/limits
    • 检查全局限制:/etc/security/limits.conf 中的 nofile 设置。
  • 解决:修改 /etc/security/limits.conf 调大 soft nofile 和 hard nofile,并重启服务生效。对于高并发场景,建议设置为 65535 或更高。

2. 本地端口范围(Local Port Range)不足

如果你的应用是作为客户端去连接上游服务(如调用数据库、Redis、其他微服务),或者在某些反向XX模式下,每次新建连接都需要占用一个本地 ephemeral port(临时端口)。

  • 原理:Linux 默认可用的临时端口范围通常是 32768-60999,约 2.8 万个端口。如果并发连接数超过这个范围,且 TIME_WAIT 状态积累过多,就会报 Cannot assign requested address 错误。
  • 排查方法:
    • 查看当前可用端口范围:cat /proc/sys/net/ipv4/ip_local_port_range
    • 查看处于 TIME_WAIT 状态的连接数:netstat -an | grep TIME_WAIT | wc -l
  • 解决:扩大端口范围,例如将 ip_local_port_range 调整为 1024 65535。同时优化 TCP 参数,提速 TIME_WAIT 回收(见下文第 4 点)。

3. 内核网络参数限制(TCP Stack Limits)

Linux 内核本身对并发连接有一些硬性或软性限制,尤其是在处理大量短连接或高频握手时。

  • 关键参数:
    • somaxconn:监听队列的最大长度。如果 Web 服务器(Nginx/Tomcat)配置的 backlog 大于此值,多余连接会被丢弃。
      • 检查:sysctl net.core.somaxconn
      • 解决:sysctl -w net.core.somaxconn=65535
    • tcp_max_syn_backlog:半连接队列(SYN Queue)的大小。在高并发 SYN 攻击或正常高并发下,如果此值过小,会导致 SYN 丢包,连接失败。
    • tcp_fin_timeout:FIN-WAIT-2 状态的超时时间。如果设置过长,会占用更多资源。
  • 解决:在 /etc/sysctl.conf 中调整这些参数并执行 sysctl -p 生效。

4. TIME_WAIT 堆积

虽然 CPU 空闲,但如果存在大量处于 TIME_WAIT 状态的连接,它们会占用本地端口和内存,并且不会立即释放。这在 HTTP 短连接场景(如传统 REST API)中尤为明显。

  • 排查方法:
    netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

    观察 TIME_WAIT 的数量是否异常高。

  • 优化策略:
    • 开启 TCP Keepalive:减少无效长连接的资源占用。
    • 启用 TCP Fast Open (TFO):减少三次握手开销(需内核支持)。
    • 调整 tcp_tw_reuse:允许将处于 TIME_WAIT 的 socket 用于新的 outgoing 连接(注意安全性,仅限可信内网环境)。
      • sysctl -w net.ipv4.tcp_tw_reuse=1
    • 改用长连接:如果是内部服务调用,务必使用 HTTP/2、gRPC 或数据库连接池,避免频繁建连断连。

5. 云厂商安全组/网络 ACL 限制

国内主流云厂商(阿里云、腾讯云、华为云等)的安全组和网络 ACL 并非无限放行。虽然它们通常不直接限制“连接数”,但某些底层网络设备或负载均衡器(SLB/CLB)可能有自己的会话表限制。

  • 注意:如果你使用的是云服务器 ECS 直连公网,一般不涉及此问题。但如果通过 SLB 转发,需检查 SLB 实例的规格限制(如每秒新建连接数 CPS、并发连接数 CCS)。ECS 侧连接数满,可能是 SLB 侧先达到了其规格上限,导致后续连接被丢弃或延迟极高,表现为 ECS 接收不到新连接。
  • 排查:检查云监控中的 SLB 指标,对比 ECS 的网络流入流量和 SLB 的流出流量是否匹配。

6. 应用层线程池/连接池耗尽

有时候“连接数达到上限”指的是应用内部的连接池(如 JDBC 连接池、HTTP Client 连接池)满了,而不是操作系统层面的 TCP 连接满了。

  • 现象:应用日志中出现 Connection pool exhausted 或 Timeout waiting for idle connection。
  • 原因:业务逻辑中存在慢查询、死锁或未及时关闭的连接,导致连接池中的连接被长时间占用,新请求无法获取连接。
  • 排查:检查应用监控(如 Arthas、JMX、APM 工具),查看线程堆栈,确认是否有线程阻塞在等待数据库或外部 API 响应。

快速排查命令汇总

# 1. 检查文件描述符限制
ulimit -n

# 2. 检查当前打开的文件句柄数(替换 <pid> 为实际进程ID)
lsof -p <pid> | wc -l

# 3. 检查网络连接状态分布
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

# 4. 检查内核网络参数
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
cat /proc/sys/net/ipv4/ip_local_port_range

# 5. 查看实时连接趋势(需要 ss 命令)
ss -s

总结建议

  1. 优先检查 ulimit -n 和 lsof,这是最容易被忽视且最常见的瓶颈。
  2. 其次检查 TIME_WAIT 数量,如果是短连接架构,考虑扩大端口范围或启用 tcp_tw_reuse。
  3. 调整 somaxconn 和 tcp_max_syn_backlog,确保内核队列足够深。
  4. 最后审视应用架构,是否应该从短连接改为长连接,是否使用了合理的连接池管理。

记住:CPU 空闲不代表系统健康,它只是说明你没有在进行繁重的计算,但可能正在被 I/O、网络或系统资源限制所卡住。