半连接耗尽时,新连接连三次握手都走不完,客户端只看到超时和SYN反复重传;全连接耗尽时,握手早已完成,连接躺在accept队列里没人取,客户端连得上却拿不到响应。一句话概括:前者是”门没开”,后者是”进了门没人接待”。
两者在监控上常常都表现为”连接数打满、业务超时”,但根因、处置动作完全不同,搞反了方向,加机器、加带宽都白费。
卡点位置不同,症状自然不同
TCP建连像两个人隔着网络握手:客户端伸手(SYN),服务端回握(SYN+ACK),客户端再确认(ACK),两个队列分别管这两段路。
半连接耗尽:卡在握手的第二步
服务端收到SYN后,会把这个”半成品”连接放进半连接队列(SYN队列),等对方回ACK,队列满了,新来的SYN就无处安放。
- 没开syncookies时,内核直接丢掉新SYN,客户端按
tcp_synack_retries重试,默认5次、累计一分多钟,最后报连接超时。 - 开了syncookies后,内核不占队列也能把握手走完,于是压力顺手推给了全连接队列这也是很多”半连接看着没事、全连接却爆了”的由来。
客户端侧的典型体感是:curl卡住不动,telnet端口半天没反应,日志里一堆”Connection timed out”。
全连接耗尽:握手完成了,业务却没接上
三次握手全部走完,连接进入ESTABLISHED,被塞进全连接队列(accept队列),等着应用调用accept()取走,应用取不过来,队列就满了。
此时服务端的处理取决于内核参数:默认丢弃新到的ACK,客户端以为连上了,发数据后一直等不到回应,表现为”连得上、没响应”;若把net.ipv4.tcp_abort_on_overflow设为1,服务端直接回RST,客户端看到的是”Connection reset by peer”。
一张表看清差异
| 对比项 | 半连接耗尽 | 全连接耗尽 |
|---|---|---|
| 握手进度 | 停在第二步 | 三步已完成 |
| 服务端连接状态 | SYN_RECV大量堆积 | ESTABLISHED堆积在队列 |
| 客户端感受 | 连不上,超时 | 连上了,无响应或RST |
| 抓包特征 | SYN反复重传,无SYN+ACK | ACK之后没有业务数据回包 |
| 应用层是否有感 | 基本无感 | 应用明显变慢或不accept |
| 主要瓶颈 | 内核SYN队列与攻击流量 | 应用处理能力与backlog配置 |
半连接队列满了和全连接队列满了有什么区别
这是运维群里出现频率极高的问题,答案要分三个视角看。
从客户端角度看
半连接耗尽,客户端连”进门”这一步都没完成,报错集中在连接超时;全连接耗尽,客户端拿到了连接句柄,卡在读响应上,超时时间往往更长,也更容易被误判成”后端接口慢”。
从服务端角度看
登录机器执行ss -s或netstat -an | grep -c SYN_RECV,如果SYN_RECV数量持续高位,且远高于正常波动,多半是半连接方向出了问题;如果ss -lnt里监听端口的Recv-Q持续接近Send-Q,说明全连接队列长期处于水位线之上。
netstat -s | grep -i listen能看到两类计数:times the listen queue of a socket overflowed对应全连接溢出,SYNs to LISTEN sockets dropped对应半连接方向的丢弃,这两个数字谁在涨,方向就清楚了。
从抓包角度看
在服务端抓包,半连接耗尽时能看到客户端SYN一次次重发,服务端始终没有SYN+ACK;全连接耗尽时,三次握手完整,之后客户端发来请求数据,服务端迟迟不回或直接RST,这个差别用tcpdump -i eth0 port 8080 -nn几分钟就能确认。
Linux全连接队列溢出怎么排查
先定位水位,再谈调参,顺序不能反。
三条命令先看清现状
ss -lnt:Recv-Q是当前排队连接数,Send-Q是该队列上限,Recv-Q贴着Send-Q不掉,就是溢出。cat /proc/net/netstat | grep -i listen:看ListenOverflows、ListenDrops是否在持续增长。dmesg | grep -i "syn flooding":出现”possible SYN flooding on port xxx”,说明内核已经判定SYN方向异常。
内核参数怎么调
net.core.somaxconn决定listen backlog上限,很多发行版默认偏保守,高并发服务应上调;应用层listen传入的backlog要同步放大,否则内核会截断。net.ipv4.tcp_max_syn_backlog控制半连接队列容量,配合syncookies使用更稳。net.ipv4.tcp_syncookies=1建议常开,它是半连接被攻击时的兜底手段。net.ipv4.tcp_abort_on_overflow默认0,改成1能让客户端快速失败而不是干等,但会放大错误率,需谨慎。
应用层才是根因
多数情况下,全连接队列满不是内核参数小,而是应用accept()太慢:单线程处理、accept后同步做耗时初始化、或者用了阻塞式框架,队列只是一个缓冲垫,垫子再厚也扛不住持续的生产慢于消费。
服务器SYN_RECV很多但业务正常是怎么回事
这种情况确实存在,不必一见SYN_RECV就紧张。
- 短时突发流量、扫描器探测、监控探活,都会让SYN_RECV短时抬升,几分钟内自然回落。
- 开了syncookies后,合法连接照样能建立,业务指标不受影响。
- 判断标准是”持续时长+业务指标”,两者同时恶化才算事故;只看一个瞬时数字容易误判。
真正需要警惕的是SYN_RECV长期高位、同时伴随新建连接失败率上升,那说明队列容量和清洗能力都不够了。
高并发场景下怎么选处置动作
云服务器连接数打满要不要加钱升配
先分清是规格瓶颈还是攻击,如果ListenOverflows和ListenDrops都不涨,只是CPU、内存吃紧,升配有效;如果是SYN Flood把半连接队列冲垮,升配只是把水位线抬高一点,攻击量不变的话很快再次打满,此时更该做的是流量清洗、限速和syncookies兜底,花钱之前,先看一眼netstat -s里的丢弃计数。
北京机房服务器遭遇SYN Flood怎么处理
思路与地域无关,动作有先后:
- 确认攻击特征,
tcpdump抽样看源IP是否高度分散。 - 开启syncookies,临时收紧
tcp_max_syn_backlog以外的防线,如接入层限速。 - 联系机房或云厂商侧做流量清洗,源IP分散的攻击在本机层面很难根治。
- 业务侧缩短超时、限制单IP并发,把影响面压到最小。
行业共识认为,半连接方向的攻击靠单机参数硬扛收益有限,清洗要放在上游。
分清两个队列,处置才有方向:半连接耗尽看攻击与SYN队列,全连接耗尽看应用accept与backlog。监控上把SYN_RECV和监听队列Recv-Q一起盯,比只盯连接总数有效得多。
半连接耗尽与全连接耗尽的常见问题
两个队列会同时耗尽吗
会,syncookies开启时半连接压力会转化成全连接压力,攻击叠加应用处理慢,两个队列可能同时见底,此时丢弃计数会同时上涨。
只调大backlog能解决问题吗
不能,backlog是缓冲,不是处理能力,业内专家指出,队列长期高位说明消费端跟不上,扩容应用或优化accept逻辑才是根本。
怎么快速判断是哪一种
执行ss -lnt看Recv-Q是否贴近Send-Q,再看netstat -s里两类丢弃计数谁在涨,两分钟内可定性。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/786213.html


