香港服务器并发连接数上不去怎么办:从进程、线程到连接池排查瓶颈

先别急着调大连接数:先确认“卡在哪一层”
在香港服务器上看到连接数长期上不去,最常见的误判是把“TCP 已建立连接数”直接等同于“应用并发能力”。实际上,连接可能卡在监听队列、进程接收环节、线程池、应用任务队列、数据库连接池,或者某个共享锁上。连接数低,并不代表服务器有余量;也可能是请求在更早的位置被拒绝、超时或排队。
排查时建议按以下顺序进行:先确认客户端请求是否真正到达监听端口,再看监听队列和进程模型,然后检查线程或事件循环,接着核对应用连接池与任务队列,最后检查文件描述符、CPU、内存、锁和下游依赖。每次只调整一个变量,并用相同请求模型复测,否则很难判断哪项修改真正有效。
先区分四种“并发数”
TCP连接数不等于请求并发数
一个客户端可以通过长连接发送多个请求,也可能建立连接后长时间保持空闲。因此,以下指标含义不同:
| 指标 | 代表含义 | 常见误判 |
|---|---|---|
ESTABLISHED | TCP连接已经建立,可能正在传输,也可能处于Keep-Alive空闲状态 | 认为每条连接都占用一个应用工作线程 |
| 监听队列 | 已到达监听端口、但尚未被应用及时接收的连接 | 只看已建立连接,忽略连接正在排队 |
| 工作线程或进程数 | 能同时处理任务的执行单元 | 认为线程数越多并发能力越强 |
| 应用连接池 | 应用访问数据库、缓存或其他后端时可使用的连接数量 | 把前端连接数上限直接设置成后端连接池上限 |
| 任务队列 | 已被接收但尚未得到处理的请求或任务 | 只增加队列长度,导致等待时间更长 |
例如,前端已经建立了大量TCP连接,但应用线程都在等待数据库连接,此时继续增加前端连接上限不会提高吞吐,反而可能让更多请求进入等待队列。
先记录基线,避免凭感觉调参
准备排查前,应保留当前应用配置、启动参数和服务版本信息,并记录一组稳定请求下的基线数据:
- 成功率、超时率和主要错误码;
- 请求平均耗时以及高分位耗时;
- 已建立连接数、监听队列长度和连接状态;
- 应用进程数、线程数、CPU、内存和文件描述符使用量;
- 线程池、任务队列、数据库连接池的活动数、空闲数和等待数。
如果使用压测工具,应固定请求路径、请求体、连接复用方式和并发模型。短连接和长连接的结果不能直接比较,静态接口和包含数据库操作的接口也不能混用。
第一优先级:确认连接是否到达应用
以下命令适用于常见Linux系统,主要依赖iproute2和procfs,属于只读检查。需要将服务名和端口替换成实际值。
# 查看系统整体Socket统计
ss -s
# 查看监听端口及监听队列
ss -lntp
# 查看已建立连接数量
ss -Htan state established | wc -l
# 查看服务状态;仅适用于使用systemd的系统
systemctl status app.service --no-pager
ss -lntp中的监听项通常包含接收队列和发送队列信息。对于LISTEN状态,接收队列可用于观察当前等待应用接收的连接,发送队列通常反映监听队列上限,但具体显示方式应结合当前Linux版本和内核文档判断,不要只凭一列数值下结论。
如果监听队列在压力持续期间不断增长,同时应用进程CPU较高、日志出现接收超时或请求延迟上升,通常说明应用没有及时执行accept或接收循环被阻塞。若监听队列很低,但客户端仍然大量连接失败,则应继续查看:
- 应用是否仍在监听正确端口;
- 工作进程是否反复退出或重启;
- 服务是否只绑定了本地地址;
- 应用日志中是否出现启动失败、文件描述符不足或资源耗尽;
- 客户端访问的端口是否与当前服务实际监听端口一致。
Linux系统还可以查看监听溢出计数。命令是否可用取决于系统是否安装nstat以及内核暴露的计数器名称。
# 先确认命令是否存在
command -v nstat
# 查看可能与监听队列有关的计数器
nstat -az 2>/dev/null | grep -Ei 'ListenOverflows|ListenDrops'
这些计数器通常是累计值,不能只看一次就判断故障。应在相同压力窗口前后各记录一次。如果计数持续增长,说明连接接收能力或监听队列处理存在问题;如果计数不增长,则应把重点转向应用内部线程、连接池和任务队列。
第二优先级:核对进程模型
进程数量决定了第一层并发边界
不同应用模型的并发含义不同:
- 多进程模型:每个工作进程独立处理请求,进程数通常决定了可并行执行的基础容量。同步阻塞模型中,一个进程可能在等待磁盘、数据库或远程服务时无法处理其他任务。
- 多线程模型:一个进程内由多个线程处理任务。线程被阻塞时仍然会占用线程池名额,线程数量过少会排队,过多则可能带来上下文切换、锁竞争和内存压力。
- 事件循环或异步模型:少量线程可以管理大量连接,但某个事件循环线程一旦执行了阻塞式数据库调用、文件操作或复杂计算,可能同时拖慢大量连接。
- 混合模型:常见形式是多个进程、每个进程包含线程池或事件循环。此时总并发不是简单地看某一个参数,必须分别检查每一层。
先查看进程和线程概况:
# 查看CPU占用较高的进程及线程数量
ps -eo pid,ppid,stat,pcpu,pmem,nlwp,cmd --sort=-pcpu | head -n 20
# 查看指定进程的父子关系
pstree -ap 1234
# 查看指定进程的线程信息
ps -L -p 1234 -o pid,tid,stat,pcpu,comm --sort=-pcpu
将1234替换为实际应用进程PID。判断时重点关注以下结果:
- 只有一个工作进程,且该进程经常处于阻塞状态:应确认应用是否采用同步模型,以及是否存在可扩展的工作进程配置。
- 工作进程数量正常,但所有工作进程CPU接近满载:瓶颈更可能是计算、序列化、加密、正则处理或业务代码,而不是连接数上限。
- CPU不高,但大量线程处于等待状态:重点检查数据库连接池、锁、文件操作和外部服务调用。
- 线程数量已经很多,但活动线程少、等待线程多:继续加线程通常不能解决问题,反而可能加剧调度和锁竞争。
- 异步应用只有一个事件循环线程,且该线程CPU持续很高:应排查是否存在同步阻塞调用或单线程计算任务。
如果系统安装了sysstat,可以使用pidstat观察一段时间内的线程状态变化:
# 每1秒采样一次,共采样5次;适用于已安装sysstat的Linux系统
pidstat -t -p 1234 1 5
不要只看瞬时CPU值。并发问题经常表现为一段时间内线程持续等待、队列持续增长,或者某些工作进程完全没有获得执行机会。
进程和线程参数不要同时大幅增加
如果确认同步工作进程数量不足,可以先调整工作进程数,再使用相同请求模型验证。若确认线程池中的线程都在处理独立的阻塞任务,才考虑增加线程数。
调整前应评估:
- 每个进程和线程的内存消耗;
- 每个工作单元可能打开的文件描述符数量;
- 数据库和缓存连接池能否承受更多并发;
- 下游服务是否会因为请求量突然增加而限流;
- 服务是否支持平滑重载,还是必须重启。
一次同时增加进程数、线程数和连接池上限,会把多个变量一起改变,也可能让数据库、缓存或系统文件描述符先达到上限。更稳妥的方式是一次只改一项,保存原值,并在异常时恢复原配置。
第三优先级:检查线程池和事件循环是否被占满
应用通常不会为每个连接无限创建线程,而是通过线程池、执行器或协程调度器处理请求。需要关注的不是“配置了多少线程”,而是以下关系:
- 活动线程数是否长期等于上限;
- 等待任务数是否持续增长;
- 任务等待时间是否超过请求处理时间;
- 线程是否在等待数据库、锁或外部服务;
- 线程任务是否执行了不应阻塞事件循环的操作。
常见判断如下:
| 观察结果 | 更可能的原因 | 优先处理方向 |
|---|---|---|
| 活动线程达到上限,等待任务持续增加 | 执行单元不足或单个任务耗时过长 | 分解慢任务,减少阻塞,再评估线程数 |
| 活动线程不满,但请求仍排队 | 任务可能卡在其他队列、连接池或锁上 | 检查连接池等待数和锁等待 |
| 事件循环CPU很高,其他资源不高 | 单线程计算或同步调用阻塞事件循环 | 将阻塞操作移出事件循环 |
| 线程数很多但CPU不高,延迟反而升高 | 线程争用、上下文切换或全局锁竞争 | 降低并发度,定位临界区 |
| 请求耗时随并发增加快速上升 | 下游资源或共享资源已饱和 | 检查数据库、缓存、文件和锁 |
应用指标名称会因框架不同而变化,可能叫active、busy、pending、queue size或wait time。如果应用没有这些指标,应优先补充监控,而不是仅依赖操作系统层面的连接数。
第四优先级:检查数据库和其他后端连接池
连接池是“连接数上不去”中最容易漏掉的一层。前端可以建立很多连接,但每个请求到达数据库操作时都要等待一个可用后端连接。如果池中活动连接数长期等于最大值,等待数不断增长,基本可以确认瓶颈不在前端TCP连接。
需要查看每个连接池的:
- 最大连接数;
- 当前活动连接数;
- 空闲连接数;
- 等待获取连接的请求数;
- 获取连接的平均和高分位等待时间;
- 连接创建失败、超时和回收次数;
- 单次使用连接的时间。
判断时要区分两种情况:
- 连接池上限太小:后端本身仍有容量,查询耗时稳定,连接池等待明显。此时可以在确认数据库最大连接数、应用文件描述符和事务行为后,小幅提高池上限。
- 后端已经饱和:连接池满、查询耗时升高、数据库CPU或锁等待增加。此时继续增大连接池只会让更多查询并行进入数据库,通常会增加排队和超时。
还要检查连接是否被业务代码正确归还。连接泄漏常见表现是活动连接逐渐增加,空闲连接持续减少,最终所有请求都在等待池。应优先修复异常分支、事务未提交或未回滚、连接未关闭等问题,而不是简单提高最大连接数。
对于缓存、消息队列或外部API客户端,也要采用相同方法检查。一个请求可能先占用线程,再等待数据库连接,最后等待外部服务响应;只有找到最长的等待环节,调参才有意义。
第五优先级:检查队列和共享资源
队列增长说明处理速度低于进入速度
队列可以出现在多个位置:
- TCP监听队列;
- Web服务器到应用的转发队列;
- 应用线程池任务队列;
- 数据库连接池等待队列;
- 消息消费队列;
- 某个业务锁的等待队列。
队列持续增长时,不应第一时间增加队列长度。队列只是暂存请求,不能提高实际处理能力。若每个请求的等待时间已经很长,扩大队列可能只是把“快速失败”变成“长时间超时”。
排查时可按队列所在位置判断:
- 监听队列增长:应用接收连接不及时,检查进程状态、工作进程和事件循环。
- 线程池队列增长:执行单元不足,或任务本身过慢,检查线程栈和下游等待。
- 连接池等待增长:后端连接不足或后端已饱和,检查池上限和后端响应时间。
- 消息队列增长:消费者处理速度不足,检查消费进程、批量处理和下游依赖。
- 锁等待增长:共享资源存在串行化,检查临界区和全局锁。
文件描述符和系统资源也会限制连接
Linux下每条网络连接、日志文件、管道和后端连接都可能占用文件描述符。可以用以下命令进行只读核对:
# 查看进程级文件描述符上限
cat /proc/1234/limits | grep -i 'open files'
# 粗略统计进程当前打开的文件描述符数量
ls -U /proc/1234/fd 2>/dev/null | wc -l
# 查看systemd服务设置的文件描述符限制
systemctl show app.service -p LimitNOFILE
如果当前打开数量接近限制,应先确认这些描述符是否都是正常连接,还是存在日志文件、套接字或管道泄漏。只有在确认连接规模合理、应用确实需要更多描述符,并且系统和下游服务都能承受的情况下,才考虑提高限制。
修改服务级限制前,应备份当前服务配置,确认服务管理方式,并准备回滚原值。修改后如果需要重启,必须考虑现有连接是否会被中断;支持平滑重载的服务应优先使用官方支持的重载方式。不要直接覆盖未知配置文件,也不要在未确认服务名称的情况下执行批量重启。
同时观察基础资源:
# 查看CPU、运行队列和内存概况
vmstat 1 5
# 查看进程实时资源消耗
top -H -p 1234
如果系统存在交换、内存回收、CPU运行队列过长或线程频繁切换,连接数上限并不是当前主要问题。此时应先减少单请求内存、修复资源泄漏、缩短阻塞操作或降低无效并发。
常见结果与对应修复方向
连接数低、客户端连接失败
先确认服务是否监听正确端口,应用进程是否反复重启,监听队列是否溢出,以及日志中是否出现文件描述符不足。若连接在建立前就失败,增加应用线程池通常不会产生效果。
连接数高、请求吞吐低
检查其中有多少是空闲长连接,再查看活动请求数、工作线程状态和请求耗时。如果大量连接只是Keep-Alive空闲连接,说明TCP连接数不能代表业务并发;如果活动请求集中等待数据库或外部服务,应优先处理连接池和下游等待。
CPU高、队列也在增长
如果所有工作进程或线程都持续占用CPU,增加并发执行单元可能只会让竞争更严重。应先定位高CPU代码、序列化、加密、压缩或复杂查询,再决定是否优化业务逻辑或调整进程模型。
CPU不高、线程大量等待
这种情况更像I/O、连接池、锁或外部服务瓶颈。重点看线程栈、池等待时间、事务持续时间和共享资源锁等待,不要仅根据CPU空闲就判断“服务器还有很多并发能力”。
提高线程数后连接数增加,但超时变多
这通常表示原先隐藏的后端瓶颈被放大了。增加线程让更多请求同时进入数据库、缓存或外部接口,导致下游排队。应回滚线程调整,限制无效并发,并减少单请求持有连接和锁的时间。
调整后的验证方法
每次修改后,都要使用与基线相同的请求和连接模型进行验证,至少对比以下指标:
- 成功率和超时率是否改善;
- 平均耗时和高分位耗时是否下降;
- 监听队列、应用任务队列和连接池等待是否停止增长;
- 活动线程是否仍长期达到上限;
- CPU、内存、文件描述符是否出现新的饱和;
- 数据库、缓存或其他后端的响应时间是否恶化;
- 连接释放是否正常,是否出现活动连接只增不减。
如果连接数上升了,但请求延迟、错误率或后端等待同时变差,这不属于有效优化,应恢复原参数。只有当相同压力下吞吐提高、队列不再持续增长、错误率没有恶化,并且资源仍处于可控范围,才能保留调整。
最容易遗漏的复核点是“连接是否被正确释放”。无论是前端TCP连接、线程池任务、数据库连接、文件描述符还是业务锁,只要有一层只申请不归还,并发通常会先表现为连接数上不去,随后变成队列堆积和请求超时。排查完成后,应在一段完整业务周期内继续观察建立、使用、释放三个阶段,而不是只看调参后的瞬时连接数。