没遇到过服务器网络连接数异常的人,很难体会那种明明CPU、内存、磁盘都正常,但服务却卡得要死的感觉是啥样的。我在一次处理线上接口超时问题时,折腾了一圈才发现是服务器上的TIME_WAIT连接堆积到十几万,把端口资源吃光了,业务请求根本建立不了新连接。自那以后,我但凡排查性能问题,第一件事就是连上服务器看一眼网络连接数——这个指标看着基础,但它往往能最直接地反映出连接池耗尽、流量异常、被扫描攻击、负载均衡转发异常等问题。
这篇文章会围绕“在服务器上查看网络连接数”展开,覆盖Linux和Windows两套主流环境下的常用命令、统计状态、聚合分析方法,以及我是怎么利用这些数据做综合定位的。适合系统运维、后端开发、SRE、刚入门的小白,也包括那些和我一样“遇到问题才想起来看网络”的朋友。
1. 先搞清楚服务器上的连接是怎么暴露问题的
1.1 网络连接数不是“一个数字”,而是一堆有用状态
很多刚做运维的朋友上来就执行netstat -an | wc -l,然后把结果当成“连接数”来报——这个数字只能说明当前有多少条连接条目,但实际价值有限。真正的分析要看连接的五元组、状态、所属进程和端口分布,只有把这些维度拆开了,才能判断服务器目前是否健康。
一条TCP连接从建立到关闭,会经历SYN_SENT、ESTABLISHED、FIN_WAIT_1、CLOSE_WAIT、TIME_WAIT等多个状态。在服务器上执行一个简单的ss -s,就能看到当前所有TCP socket状态的汇总统计。我在日常巡检里习惯把这个输出作为“第一眼指标”:如果ESTABLISHED状态数骤降,可能是上游流量没进来;如果TIME_WAIT数居高不下,说明短连接场景下连接没有及时回收;如果SYN_RECV堆积,那基本可以怀疑是半连接攻击或者后端服务响应过慢。
真正理解这些状态代表什么,遇到连接数异常时就不会慌。举一个典型例子:Tomcat部署的应用报“Connection reset”或者“Broken pipe”,这时候你去服务器上看网络连接,十有八九能看到大量CLOSE_WAIT,这通常说明应用代码里没有正确关闭Socket连接,底层连接已经断开,应用自己还不知道,还在占着文件描述符。单纯加连接数是解决不了这类问题的,必须先定位到CLOSE_WAIT集中在哪个进程,再去查对应代码逻辑。
1.2 查看连接数的常用命令选型
Linux服务器上现在主流的两条命令是netstat和ss。我个人的习惯是:优先用ss,ss直接读取内核的socket的信息,不依赖/proc/net/tcp解析,在高连接数场景下速度要快一个量级,而且输出信息更全。你只要在服务器上体验一次,先跑netstat -an | wc -l,再跑ss -s,对比一下响应时间,应该就能感受到差距。不过netstat也不是完全没用了,很多老服务器、旧脚本还有依赖,而且有些最小化安装的Linux发行版默认只带netstat,装ss还得自己搞一下iproute2。
Windows环境下我一般用netstat -ano,这里的-o能显示PID,方便我顺着进程号去任务管理器里手撕对应的应用。Windows的命令提示符下没有ss,但资源监视器里自带“网络”标签页,可以看到TCP连接列表和按进程统计的连接数,图形界面在那个环境下反而更适合一眼扫出异常。后期系统比如Windows Server 2019以上还能配合PowerShell里的Get-NetTCPConnection来查询,但它的输出格式我个人觉得不如 netstat 直观,日常反而用得少。
2. 动手实操:用这些命令查看网络连接数
2.1 Linux环境下查看TCP连接状态汇总
要快速了解一台服务器的整体网络连接概貌,我通常先用ss -s。这条命令会输出一段类似这样的文字:TCP协议统计会显示“estab”、总连接数、time wait、close wait等字段。不需要额外参数,执行速度极快,即使机器上已经建立了上百万条连接,也能秒级返回,光这一点就已经取决于现场效率了。
如果需要看得更细,那就用ss -ant,这条命令把当前所有TCP连接按状态、本端地址、对端地址、队列等字段全列出来。这里注意:如果服务器是生产环境且连接数上了万,直接执行ss -ant会把终端刷得没完没了,我一般先| head -n 20看个开头,或者配合后面的统计命令直接做聚合。真正的分析手段不是看每条连接记录,而是看统计结果,比如我按状态分个组:
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn执行后能得到一个各状态分布数量的表,比如15 LISTEN、3200 ESTAB、8000 TIME-WAIT这样。这一招在我排查问题时经常是第一步,上来就知道当前连接基本都是哪种状态,后续的排查方向也就清晰了。比如看到SYN-RECV数量很高,我第一反应就是去看是不是有外部IP在扫描端口,或者被人打了SYN Flood。
检查监听端口的情况,我习惯用ss -lntp。-p参数会把对应进程的PID和进程名显式打印出来,比如users:(("nginx",pid=1532,fd=12)),调试的时候就能直接看到端口被谁占着,是不是预期内的业务进程,还是有人在你机器上跑了个奇怪的服务。这个命令等效于netstat -lntp,但响应速度更让人痛快。
2.2 Windows环境下如何查看和筛选连接
Windows服务器没有ss,但netstat足够解决日常问题。我通常在cmd或者PowerShell里执行:
netstat -ano输出里最后一列是PID,第二列是本机IP加端口,第三列是远端地址,第四列是状态。要看某个端口被谁占用,比如排查IIS绑定的80端口,直接配合findstr过滤:
netstat -ano | findstr :80这样能把所有涉及80端口的连接都打出来,再根据PID到任务管理器里确认是哪个进程。如果还想确认某个PID名下具体有哪些连接,可以再过滤一次PID列。多个筛选条件结合,排查进程的网络行为就基本足够了。
如果是Windows Server 2012以上的系统,我也偶尔用PowerShell:
Get-NetTCPConnection -State Established | Group-Object State | Select-Object Name, Count这种方式的筛选和分组能力比 netstat 更灵活,但个人不是一个重度PowerShell用户,平时不会动不动就去写脚本。对于临时看几眼,我还是觉得 netstat 够用。如果要在Windows上做复杂的连接统计,比如按远端IP聚合连接数,我反而会导出数据后再用Excel或者脚本做二次分析,而不是在命令行里死磕。
2.3 统计脚本:把连接数变成可分析的表格
查看连接数这一步很简单,但要在生产服务器上快速判断“连接数高了是不是有鬼”,光靠肉眼看几百上千行输出不现实。我的做法是用一条管道命令把连接数据聚合起来,这里分享几个高频使用的统计命令。
按客户端IP统计连接数排名前20:
ss -ant | awk '{print $5}' | awk -F':' 'NF>=2 {print $1}' | sort | uniq -c | sort -rn | head -n 20这条命令的结果会告诉我当前哪些IP正在大量连接服务器。比如以前有一个场景,我发现某一个内网IP的连接数占掉了一半以上,查下去发现是测试环境的一个压测脚本没关,一直在连生产库。没有这条命令,我估计要翻半天连接列表才能定位到。
按状态+目标端口组合统计,也很实用:
ss -ant | awk 'NR>1 {print $1, $4}' | awk -F' ' '{port=$2; sub(/.*:/, "", port); print $1, port}' | sort | uniq -c | sort -rn | head -n 30这样能直接看到每个端口的连接状态分布,比如80端口上基本都是ESTAB,3306端口上有大量TIME_WAIT,说明前端应用在频繁连数据库且是短连接,可以考虑改成连接池。如果发现443端口上同时有大量SYN_RECV,就要警惕是不是有人直接对HTTPS端口发起攻击了。
统计脚本我一般只放在服务器上的一个固定目录,比如/usr/local/bin/conn_stat.sh,避免每次敲一长串管道符,容易敲错也不方便记忆。
3. 连接数的综合分析:从数值现象到问题定位
3.1 结合连接状态判断业务健康状况
拿到连接数统计结果后,第二轮问题就是:“这些数字代表什么?”我是从以下几个维度来判断的。
如果ESTABLISHED连接数持续稳定在一个范围,比如Web服务网关维持在几百上千,说明后端服务能正常接受转发;如果这个数字突然跌到几乎为0,但负载均衡又说流量没有下降,那我就会怀疑是不是后端应用挂了、端口没监听,或是防火墙把连接拦了。
TIME_WAIT连接数量大,常见于高并发短连接的场景。TIME_WAIT本身是TCP正常关闭的一方进入的状态,用于保证延迟的数据包不会干扰新连接。不过大量TIME_WAIT会占用内存和本地端口,一旦本地端口耗尽,新连接就无法发出,我遇到过最极端的情况是TIME_WAIT占满了几万个端口导致业务几十秒不可用。处理此类问题的常用思路是开启net.ipv4.tcp_tw_reuse(但只在客户端场景下管用,如果服务器本身就是服务端,TIME_WAIT主要出现在主动断开的一方)以及调整net.ipv4.ip_local_port_range扩大可用端口范围。
CLOSE_WAIT数量长时间高于个位数,需要特别注意。CLOSE_WAIT意味着对端已关闭连接,但本端进程没有完成关闭流程,说白了就是程序里的连接没有正确close。如果这种状态不断累积,文件描述符很快会耗尽,应用会出现“Too many open files”报错。我处理过一起Java应用CLOSE_WAIT持续增长的问题,最后定位到是HTTP连接池没设置空闲超时,导致大量连接在服务端已被关闭但客户端还在持有。
SYN_RECV状态数量异常偏高,说明TCP三次握手只完成了两步,回包发不出去,可能原因包括:后端服务accept队列满了、防火墙丢弃了SYN_ACK包、或者遭遇SYN泛洪攻击。这种情况我会同时看CPU和网络软中断负载,如果软中断飙高,多半是在应付大量无用的握手包。
3.2 百万并发场景下的文件描述符与端口瓶颈
连接数不是一个孤立指标,它一定会反映到系统资源上。连接数一多,最先受冲击的就是进程文件描述符数量、内核端口范围和内存分配。所以查看连接数不是终点,还要再看这三个地方:
文件描述符限制方面,在Linux上执行:
ulimit -n通常默认1024可能不够生产环境用,我会评估业务场景把nofile调高到65535甚至100万。很多故障现场就是文件描述符被打满,新连接根本无法accept,看起来表现就是“连接被拒绝”或“无法分配内存”。检查时可对比当前进程的fd占用:
ls -l /proc/PID/fd | wc -l端口范围方面,客户端发起的连接需要占用本地端口,可用的本地端口数量决定了一个IP对同一目标IP的最大并发连接数。查看范围:
cat /proc/sys/net/ipv4/ip_local_port_range我见过默认设置为32768~60999,也就是总共约28000个端口,如果短连接速率极高,这个范围很容易被打满,报错是“Cannot assign requested address”。日常调优时我会把起始端口调到1024左右,扩大可用区间,但前提是要避免与常用服务端口冲突。
连接追踪表方面,iptables/nftables防火墙状态下,Linux会维护conntrack表项:
cat /proc/sys/net/netfilter/nf_conntrack_max cat /proc/sys/net/netfilter/nf_conntrack_count如果count接近max,新连接会丢包或拒绝,现象是网络时通时不通。检查和监控这两个数字,能提前避免防火墙连接表爆满导致的隐性故障。
3.3 判断服务器是被扫描还是被攻击的经验
服务器放在公网上,每天都会被扫描和试探,这是常态。判断连接数是“正常业务流量”还是“被攻击”,我会看这几个特征。
大量不同源IP都连到同一个未被业务使用的端口,比如22端口或者1433、3306等数据库端口,但业务根本用不到,这就是扫描。看统计:
ss -ant | awk 'NR>1 {print $4}' | awk -F':' '{print $NF}' | sort | uniq -c | sort -rn如果某个不常用端口出现密集的SYN_RECV或大量短连接,基本可以判定是扫描行为,处理方式是防火墙封禁该端口并限制访问源。
单个源IP对多个端口发起连接,这也是典型的端口扫描模式。我用上一步的按IP统计命令,一旦看到某个远端IP的连接数远高于其他IP,优先去确认是不是自己的业务出口IP;不是的话,直接封锁该IP,再观察业务是否有异常。
从业务正常性角度再反推:如果当前连接数总量翻了几倍,但业务后台监控显示QPS并没有上涨,那多出来的流量一定来自非业务方。此时我会把当前连接统计导出到文件,抓几个异常IP然后去查IP信誉,确定是否来自IDC扫描段、境外IP池等。综合这几个维度,就能区分出是扫描、攻击还是正常流量突增。
4. 按场景讲的连接数排查案例
4.1 场景一:Nginx服务器TIME_WAIT过多导致连接失败
这个故障现象是:用户反馈系统偶尔卡顿,打开页面时好时坏,Nginx日志里大量出现upstream timed out。我第一反应就是看Nginx所在服务器的连接状态统计,执行ss -s后发现TIME_WAIT竟有3万多。再用脚本按目标IP分组,发现大量TIME_WAIT都集中在后端的内网IP上,定位到是Nginx频繁向后端发起短连接,而每次访问后连接被快速释放。
当时的解决方式分了几步:第一步,在Nginx的upstream配置里开启keepalive 32,让Nginx和后端复用长连接,减少反复建连;第二步,开启内核参数net.ipv4.tcp_tw_reuse=1,允许客户端在安全条件下复用TIME_WAIT连接,同时把net.ipv4.ip_local_port_range扩展到10240 65535;第三步,在后端服务侧也检查了一下连接池配置,确保服务端不是主动关闭连接的一方。调完以后,TIME_WAIT数量大幅下降,接口超时告警也消失了。
这里要强调一个容易搞错的点:TIME_WAIT的数量多并不总是坏事,只有当本地端口耗尽或者连接数影响到新建连接时才需要干预。很多文章一看到TIME_WAIT多就让人改参数,但如果没有端口耗尽或者明显性能下降,其实可以不用管,系统最终会按2MSL时间自动清理。
4.2 场景二:数据库服务器CLOSE_WAIT堆积引发的应用变慢
有一次某个应用的数据库连接数突然爬升,但数据库自身的QPS并没有增长,数据库服务器的CLOSE_WAIT数量却一直在涨。我在数据库服务器上按进程和状态分了一下组,发现大量CLOSE_WAIT集中在来自应用服务器的连接上,说明应用服务器在某些场景下没有正确关闭JDBC连接,或者连接被异常中断后应用没有感知。
我当时建议开发检查连接池配置,把连接池的maxLifetime、idleTimeout和validationQuery都设置起来,确保能被定期检查和回收。同时在应用日志里查有没有进行中的事务未提交就丢掉了连接。最后定位到是某个批处理逻辑里使用数据库连接后没有放到finally里关闭,导致只有连接空闲超时到被池子淘汰时才会释放,时间一久CLOSE_WAIT越堆越多,应用线程也大量阻塞在拿连接上。
CLOSE_WAIT的问题,我的经验是百分之九十是代码层面的连接关闭遗漏,很少纯粹是内核参数的问题。所以看到CLOSE_WAIT增长,不要盲目调内核,优先去查哪个进程的连接在堆积,顺着PID去定位代码路径。
4.3 场景三:SYN_RECV暴涨,端口扫描还是半连接攻击
有一次我在例行巡检时注意到服务器上SYN_RECV连接数超过5000,而且目标端口是8020这种不常见的端口,业务端口完全没受影响。用ss -ant | grep SYN-RECV | awk '{print $4}'看了一眼目标端口分布后,发现就是单个端口被大量随机源IP尝试连接。从防火墙日志确认了进来的SYN包数量远超正常水平,基本确定是端口扫描行为。
处理思路是:先用防火墙封禁这个端口的外部访问,业务本来就不用到这个端口,直接拒绝;再顺藤摸瓜查一下这些源IP的分布,发现来自好几个国家的IP段,都指向云平台扫描;最后把系统上不需要的监听端口全都清理了一遍,并开启了fail2ban对连续多次尝试连接的IP做自动封禁。处理之后SYN_RECV数量就降下来了。
如果遇到的是真正的SYN Flood攻击,目标端口是业务端口,那就不能简单封端口了,需要配合流量清洗服务、启用SYN Cookies、调整半连接队列大小等来缓解。这类问题从连接数层面能看到信号,但解决起来需要网络架构层面的配合。
5. 进阶:用监控与自定义脚本持续跟踪连接数
5.1 自己写个快速脚本:定时记录连接状态
排查完一个问题后,我的习惯是加一个简单的监控脚本,把连接状态按时间维度记录下来,以后出问题时有历史数据可以做对比。脚本不复杂,就是一个计划任务配一段脚本:
mkdir -p /var/log/conn_stat */5 * * * * echo "$(date '+%Y-%m-%d %H:%M:%S') $(ss -s | grep -E '^TCP' | sed 's/^TCP//')" >> /var/log/conn_stat/conn.log这样每5分钟会在日志文件里追加一条当前TCP汇总状态。某天业务反馈变慢时,直接打开这个日志看时间轴上的变化,就能知道是从哪个时间节点开始连接数升高,再结合当时是否有发布操作或流量波动,定位速度会快很多。脚本虽然简单,但胜在能看到趋势,比只抓当前快照靠谱得多。
更进一步,我还会监控进程级别的文件描述符使用情况:
*/5 * * * * cat /proc/PID/status | grep -E 'FDSize|Threads' >> /var/log/conn_stat/app_fd.log比如Nginx的PID、Java应用的PID,都有自己的fd增长曲线。连接数高不可怕,怕的是连接数高但fd已经逼近上限,故障随时可能触发。
5.2 借助Prometheus、node_exporter做长期可视化
如果服务器较多,单纯靠写日志不是长久之计。我在维护多台机器的环境里,会用Prometheus加node_exporter拉取网络指标。node_exporter自带node_sockstat_TCP_alloc、node_sockstat_TCP_tw、node_netstat_Tcp_CurrEstab等指标,能直接采集当前TCP连接状态。
一条典型的PromQL查询连接数趋势:
sum(node_netstat_Tcp_CurrEstab) by (instance)Grafana上配上面板之后,能在连接数异常上涨时快速看到是哪些机器在涨、是否和告警时间吻合。相比手动执行命令,这个做法更像一个正儿八经的监控体系,适合需要值守的环境。
配置告警规则时,我通常按状态分别设置阈值:例如ESTABLISHED超过基线200%、TIME_WAIT大于20000、SYN_RECV大于1000,持续5分钟以上就告警。阈值并不是拍脑袋定的,而是看了一段时间的正常运行数据后,按3到5倍正常量级来设置的,避免日常小波动就频繁骚扰。
有一点值得提醒:node_exporter采集的是主机层面的状态,只能看总量,无法细到某个四元组。如果需要更细的分析,还是要靠ss导出的连接快照,或者用 eBPF 工具捕获连接事件。监控做的是“报警”,定位做的是“排障”,两者不能互相替代。
5.3 其他实用排查工具组合
除了ss和netstat,我在不同场景下还会用到几个工具,组合起来分析连接数会更顺手。
lsof用来精确查看某个进程打开的所有网络连接,典型的命令是:
lsof -i -P -n | grep nginx-P禁用端口名解析,-n禁用域名解析,执行起来更快,输出也更明确。连接数异常时,通过lsof能直接看出哪个进程和哪个远端IP有连接,比ss -p的信息更细腻一点。
tcpdump适合做抓包分析,比如怀疑有IP在持续连接但连接建立后立即发数据,可以抓一下包看载荷特征。不建议在流量大的生产主机上长时间全量抓包,但配合过滤条件短时间抓取,通常就能拿到关键信息。以前排查一个被中马的情况,就是通过 tcpdump 发现服务器往外连了一个海外IP的443端口,才定位到问题。
nethogs是一个按进程显示实时带宽的工具,配合连接数查看,能一眼看到哪个进程在大量占带宽和建立连接。不过它在高流量下也有点吃资源,一般只在已经怀疑到某个具体进程时才用。
这些工具我统一的观点是:会用不熟没关系,但至少知道每个工具能回答什么问题,比如ss回答“当前有什么连接”,lsof回答“这个进程打开了哪些连接”,tcpdump回答“连接里到底在传什么”,nethogs回答“哪个进程在抢占带宽”。排查时把这几个答案拼起来,全貌就出来了。
6. 常见问题排查速查表
为了日常使用方便,我把常见的连接数异常状态、可能原因和处理方向整理成了一个速查表,放在服务器运维文档里,每次同事遇到类似问题也先丢这个表给他们。
| 连接状态或现象 | 可能原因 | 初步处理方向 |
|---|---|---|
| TIME_WAIT 数量大 | 大量短连接,主动关闭方没有复用连接 | 服务端/客户端开启 keepalive,调整 tw_reuse,扩大端口范围 |
| CLOSE_WAIT 持续增长 | 应用没有正确关闭连接 | 排查业务代码连接管理,检查连接池配置 |
| SYN_RECV 异常升高 | 半连接队列满、SYN攻击、后端accept慢 | 调整 tcp_max_syn_backlog、启用 syncookies、检查后端队列 |
| ESTABLISHED 突然跌零 | 服务进程退出、端口停止监听、防火墙拦截 | 检查进程、监听端口、防火墙规则 |
| 某个IP连接数异常高 | 可能是业务出口IP或攻击源 | 确认IP归属,必要时封禁并抓包分析 |
| 端口耗尽报错 “Cannot assign requested address” | 客户端短连接过多,本地端口范围不够 | 扩大 ip_local_port_range,改造连接模式 |
| 连接时通时不通,conntrack表满 | nf_conntrack_max 到达上限 | 调整 conntrack 配置或减小超时时间 |
排查顺序我总结了三步:第一步看ss -s掌握全局状态;第二步按状态、源IP、目标端口聚合,定位异常连接特征;第三步顺着PID和抓包看具体是哪个进程在做什么。这三步走完,大多数连接数问题都能找到方向。
有个细节容易被忽略:查看连接数时最好加上时间维度。比如同样一条ss -s,5分钟前的输出和现在的输出相比较,才能判断数字是稳定缓慢增长还是突然飙升。我排查时会把第一次看到的输出保存下来,连续看几次,才下结论。不要一上来看到某个数字偏大就急着调优,很多状态在TCP正常生命周期里本来就会同时大量存在。
7. 一点个人经验之谈
写到这里,很多人可能会觉得“查看网络连接数”就是一条命令的事,但我在实际排查了各种连接类故障后,最大的体会是:连接数只是表象,真正的功夫在于结合业务形态去判断这些数字是否合理。同样是2万个TIME_WAIT,在一个高并发的短连接API服务上可能完全正常,在一个长连接业务上就是严重异常。所以不要迷信网上的“标准值”,要把自己服务器的基线建起来,平时的正常范围是多少,数值翻了多少倍,这比绝对值更有参考价值。
还有一个经验是:改内核参数前一定先备份,而且每次只改一项,观察一段时间。有些参数之间是有关联的,比如tcp_tw_reuse和连接复用,调的激进可能影响需要四元组唯一性的场景。以前我就因为同时改了两三个参数出问题后无法确定是哪个改动引入的,来回回滚花了不少时间。现在我的原则是:能通过业务层和应用层解决的,优先改代码;实在需要动内核的,小步调,留回滚点。
再分享一个小技巧:把ss的输出习惯加上-n,大部分情况下都别加-r或域名解析。排查现场域名的反解析不仅慢,还可能因为内网DNS异常导致命令卡住,反而误事。IP地址虽然看起来不直观,但配合已知的服务端口,足够判断是谁连谁了。
服务器网络连接数这个指标,属于那种看起来简单、但每个现场都可能有新花样的东西。希望你看完这篇之后,至少知道下次遇到连接数异常时,该怎么一步步查下去,少踩几个坑。