
直接看Open System Interconnection模型可能有点虚但真到连不上服务的那一刻你会发现传输层和应用层才是最磨人的地方。上篇讲完链路和网络层中篇把主机网络状态理了一遍这篇补上传输/应用层服务排查最后给三步法画个完整的句号。先说结论端口通不代表服务通服务进程活着不代表业务可用。你从客户端一路 curl、telnet、看系统日志最终追到的问题往往不是网络本身而是服务端队列满了、证书过期了、后端连接池打满了这类“看起来像网络故障”的应用层问题。这套三步法的价值就是让你不用猜照着顺序一次排干净。1. 传输层诊断服务通不通先看端口和状态1.1 端口连通性检查的常见误区遇到连不上服务很多人第一反应是 telnet 一下 IP 端口。这没错但坑全在细节里。telnet 只是确认 TCP 能不能建立连接它不负责告诉你服务是否健康。我曾经遇到一台数据库服务器telnet 1521 端口秒开客户端却一直报“监听器无响应”后来才发现是监听器进程僵死TCP 握手还能完成业务层的协议握手早就断了。所以端口测试只能作为第一层过滤别把它当最终结论。另一个常见问题是测试姿势不对。用telnet 10.0.0.5 8080卡在那不动很多人以为是网络问题其实可能是目标主机开启了防火墙丢弃了 SYN 包也可能是安全组规则没放行。遇到这种情况建议用带超时的工具timeout 5 bash -c echo /dev/tcp/10.0.0.5/8080 echo port open || echo port closed/timeout nc -vz -w 3 10.0.0.5 8080nc -vz -w 3的-w参数很关键它把连接超时限制在 3 秒内避免终端一直挂着。而且你最好在目标机器本地也测一次ss -lntp | grep 8080确认监听地址到底是0.0.0.0还是127.0.0.1。很多人把服务绑在回环地址上导致外部无法访问这种问题在网络层根本看不出来只能到传输层测完才知道。还有防火墙过滤规则的坑。云主机上一般有两层系统自身的 iptables/firewalld以及云平台的安全组。我习惯这样区分排查ss -lntp | grep -w 8080 # 本地是否有监听 systemctl status firewalld # 本机防火墙是否开启 iptables -L -n | grep 8080 # 本机规则是否放行安全组层面的问题在主机内看不到需要到云控制台查看。如果本地监听正常、本机防火墙也放行了外部还是不通先怀疑安全组和网络 ACL用同 VPC 的另一台机器再测一次能缩小一半排查范围。1.2 TCP握手与连接队列的排查思路端口通了但请求偶尔超时、连接建立特别慢这时候就该看 TCP 握手状态了。ss -ant是最常用的命令重点看SYN_RECV、ESTABLISHED、TIME_WAIT这三类状态。SYN_RECV大量堆积是一个严重信号。正常情况下服务端收到 SYN 后进入 SYN-RECEIVED 状态如果这个状态的数量持续上涨而ESTABLISHED不涨基本可以断定是 SYN 洪泛或者服务端的 accept 队列满了内核来不及把半连接转成全连接。用ss -lnt能看到当前 socket 的Send-Q和Recv-Q。在 LISTEN 状态下Recv-Q表示当前 accept 队列中等待应用 accept 的连接数Send-Q表示队列最大长度。如果Recv-Q长期接近Send-Q说明应用层处理能力跟不上连接全堵在队列里了。我遇到过一个 Java 应用刚上线时一切正常推广活动当天突然大量请求超时。用ss -lnt一看8080 端口的 Recv-Q 数值长期停在 512而 Send-Q 只有 128。这就是典型的 accept 队列溢出。调整了两个内核参数之后立竿见影sysctl -w net.core.somaxconn1024 sysctl -w net.ipv4.tcp_max_syn_backlog2048应用层也要配合。比如 nginx 的listen 8080 backlog1024Java 的ServerSocket构造函数里的 backlog 参数否则内核队列调大了应用层不认照样溢出。连接状态里还有个容易被忽略的TIME_WAIT。大量 TIME_WAIT 通常意味着短连接频繁创建关闭对一般服务来说不是致命问题但如果 TIME_WAIT 数量达到几万甚至十几万会占用大量端口和内存导致新连接无法建立。可以用net.ipv4.tcp_tw_reuse1配合tcp_timestamps来缓解注意tcp_tw_recycle这个参数在 NAT 环境下有坑别轻易动。排查握手的另一个实用工具是 tcpdump。抓包看到 SYN 发出去了但一直没收到 SYN-ACK问题大概率在链路层、防火墙或对端如果能看到 SYN-ACK 但应用连不上那就要检查本机路由和iptables了。抓包时重点关注tcpdump -nn -i eth0 tcp port 8080 -c 100 -w handshake.pcap抓完用tcpdump -nn -r handshake.pcap | head -20看一眼三次握手的包特征SYN、SYN-ACK、ACK 三个包时间戳间隔是否正常。如果 SYN-ACK 重传多次才到就是中间链路丢包。1.3 UDP服务排障的特殊性UDP 比 TCP 难排得多因为无连接、无重传出了问题经常是“静默丢失”。DNS 服务、NTP 时间同步、部分日志采集都是 UDP遇到这类服务不通telnet 和 nc 的常规 TCP 测试都用不了要换思路。先用nc -u测nc -u -z -w 3 10.0.0.5 53严格说-z对 UDP 没有意义因为 UDP 没有连接建立过程但这个命令能触发一次发送如果收到 ICMP 端口不可达说明目标端口没监听如果没反应可能是通也可能被防火墙丢包了。想确认 UDP 是否真的通了最好用应用层协议本身来验证。比如 DNS直接dig 10.0.0.5 example.com看返回NTP 就用chronyc sources或者ntpdate -q试同步。应用层能正常应答就说明 UDP 通。UDP 还有一个绕不开的话题是 MTU 和分片。UDP 包一般比 TCP 更容易触发分片MTU 不一致会导致大包丢失、小包正常。经典症状是 DNS 解析偶尔超时dig short能通但dig bufsize4096就丢包大概率是路径上的 MTU 小于 1500又禁用了 ICMPPMTUD 无法工作。排查方法是在两端各看一次ip link show eth0 | grep mtu ping -M do -s 1472 -c 3 10.0.0.5-M do是禁止分片-s 1472是 1500 减去 IP 和 ICMP 头部后的最大可用载荷。如果这个包能通MTU 基本没问题如果通则更大包不通就是路径 MTU 受限。2. 应用层服务诊断端口开着不代表业务正常2.1 HTTP状态码的意义与快速定位传输层确认没问题之后真正定位业务故障就进入应用层了。HTTP 服务最常见也最误导人。502 和 504 一字之差排查路径完全不同。502 是网关收到了后端无效响应常见原因包括后端进程崩溃、后端端口没监听、后端响应超时。排查时先看 Web 服务器错误日志nginx 的话在error.log里经常会有connect() failed (111: Connection refused)或upstream prematurely closed connection。前者是连接被拒后者是后端正处理着突然断了多半是应用 OOM 或被 kill。504 是网关超时网关本身连上了后端但后端在超时时间内没返回。这种情况先看超时配置nginx 的proxy_read_timeout默认 60 秒如果后端接口本身要跑几分钟那就是配置问题如果后端平时几十毫秒现在突然超时那就要查数据库慢查询、外部 API 依赖、线程池阻塞。500 错误最常见的原因反而不是代码逻辑而是环境差异。比如配置文件里写了某个绝对路径生产环境不存在比如依赖的 Redis 连不上抛了异常。这时候应用日志的价值远超 HTTP 状态码本身。我的排查顺序是curl -v -m 10 -o /dev/null -s -w code:%{http_code} time:%{time_total} connect:%{time_connect}\n http://10.0.0.5/health-w里的三个时间指标很有用time_connect是 TCP 建立连接耗时如果这个值很大问题在传输层或网络time_total是整体耗时如果它很大而time_connect正常问题在应用处理。用这个命令能把传输层和应用层的锅一次分清。301/302 跳转也是容易被忽视的坑。客户端访问的是https://service.example.com/api服务端返回 301 跳到http://service.example.com:8080/api然后客户端再访问这个 HTTP 地址如果防火墙只放行了 443这一步就会超时。日志里看到大量301且跳转地址的端口不对优先检查反向代理的proxy_set_header Host和协议配置。2.2 域名解析与证书问题应用层排障第一步往往是域名解析不是 curl 能通就完事。我习惯同时看本地 DNS 解析结果和系统实际使用的解析路径dig short service.example.com cat /etc/resolv.conf getent hosts service.example.comgetent hosts的结果反映了 glibc 实际使用的解析顺序它可能走了 nsswitch 里的 hosts 文件、DNS、mDNS 等各种来源。有时候/etc/hosts里有一条过期的内网解析记录dig 查出来是新的公网地址但应用实际走的还是 hosts 里的旧地址造成诡异的不通。DNS 缓存也是高频问题。systemd-resolved 默认会缓存systemd-resolve --statistics能看到缓存命中情况重启systemd-resolved或者用resolvectl flush-caches可以清缓存。Java 应用还有个特殊坑JVM 默认不刷新 DNS 缓存除非显式设置networkaddress.cache.ttl否则即使 DNS 记录改了Java 进程还拿着旧 IP 使劲连排半天才发现是 JVM 缓存。HTTPS 证书问题更隐蔽端口通、TLS 握手也能完成但客户端校验证书时报错。用 openssl 直接看服务端证书openssl s_client -connect service.example.com:443 -servername service.example.com /dev/null 2/dev/null | openssl x509 -noout -dates -subject -issuer-servername参数对应 SNI很多服务器部署了多个证书必须带上才能拿到正确的证书链。我见过部署了通配证书但绑定的域名不在覆盖范围内用 IP 访问时证书完全不匹配浏览器直接报警。遇到证书问题重点看三个东西证书有效期、证书链是否完整、证书绑定的域名是否匹配。内网环境经常喜欢自签证书这时候客户端要么信任该 CA要么把证书校验关掉。排查时先确认客户端有没有正确加载 CA 证书别一上来就怀疑网络。某次某应用升级后突然连不上内部服务排到最后一查是新的运行时环境没导入旧的自签 CATLS 校验失败和网络半点关系没有。2.3 应用日志与数据库连接的典型关系应用层很多故障根因最终落在数据库连接上。现象是接口超时、连接池耗尽、偶尔报错看起来像网络不通但实际是数据库侧出问题。排查数据库连接问题一定不要只盯着应用日志数据库侧也要一起看。比如应用日志出现Communications link failure常见原因有数据库连接超过 wait_timeout 被服务端关闭、连接池配置的 maxActive 太小、网络不稳定导致 TCP 被中间设备断开。一个很典型的场景应用连接池配置了 50 个连接数据库的 max_connections 默认只有 100多个应用实例加起来几百个连接把数据库打满。这时候从任何一台应用服务器测试数据库端口都是通的但新的数据库连接建立后数据库无法处理查询表现为超时和连接池耗尽。排查步骤可以这样# 应用侧查看连接池状态以Java/tomcat为例 jstack pid | grep -A 20 http-nio | grep JDBC # 数据库侧查看当前连接数 mysql -e show processlist; | wc -l mysql -e show variables like max_connections;如果 processlist 里的状态长期卡在Waiting for table metadata lock或者Sending data那数据库确实扛不住了需要看慢查询和锁等待。如果数据库一切正常连接数不高就要考虑应用代码里的连接泄漏。应用日志还有一个价值是时间对齐。出现故障时通常不是所有实例同时挂而是某几个实例报错。用日志时间戳和负载均衡的健康检查日志横纵对比能快速定位是哪台实例有问题再针对性的看那台的 GC 日志、线程 dump、磁盘 IO。比如磁盘 IO 满了导致 JDBC 查询长时间等待这种问题从网络层看毫无破绽只有把应用日志和系统监控放在一起才能发现。日志排障另一个高频场景是慢请求。如果 nginx access log 里 upstream_response_time 有大量请求超过 1 秒而接口本身是简单的读写操作重点看依赖的下游服务缓存、数据库、消息队列。每个环节都要抓不要想当然认为是数据库。某次排查慢接口数据库正常、Redis 正常最后发现是 HTTP 客户端连接池指向了一个已经被回收的负载均衡地址导致每次请求都要花 10 秒等长连接超时重建这种问题只有靠日志里的时间分布才能看出来。3. 排障流程总结三步法怎么串起来3.1 三步法整体框架回顾把三步法完整串一遍很多人会觉得就是“按层查”但实操中容易跳层一着急就去抓包或者重启服务。三步法的核心价值是规定了顺序让你在最短时间内排除不可能的环节。第一步是链路与网络层解决“路线通不通”的问题。IP 能不能通、路由表对不对、有没有丢包、防火墙放没放行。第二步是主机网络与传输层解决“本机网络状态和服务端口正不正常”的问题。看网卡统计、IPtables、连接状态、端口监听。第三步就是这篇的传输/应用层服务诊断解决“服务进程本身健不健康”的问题。端口通、连接建立成功但业务依然不可用就继续深入 HTTP、数据库、证书、权限、依赖服务。我习惯把三步法总结成一张流程图式的判断逻辑从客户端ping目标 IP通进入下一步不通查链路、路由、防火墙。从客户端telnet/nc目标端口通进入下一步不通查本机监听、防火墙、安全组。从客户端curl服务地址能拿到预期响应问题排查完拿不到根据错误类型查 Web 日志、应用日志、后端服务。每一层都是“通”与“不通”二分判断二分法在排障里永远是最快的。最怕的是你第一层还没确认就因为“怀疑是安全组”跑到控制台改规则改了一圈发现是应用崩了白白浪费大量时间。3.2 排障记录与信息收集的关键意识动手排障之前先花一分钟收集信息远比直接敲命令有用。我踩过的最大坑就是跳过信息收集直接猜猜错方向后还得回头补现场。必须记录的四件事故障开始时间和持续时间。这决定了查日志的时间范围也方便和其他事件关联。最近的变更。谁改了配置、谁发了版本、谁调整了防火墙变更往往是最短路径上的根因。影响范围。是个别用户、个别 IP还是全网、全部客户端。影响范围能直接缩小排查对象。现场资源状态。CPU、内存、磁盘、网络连接数在故障时的表现这些数据过时不候。网络抓包同样讲究窗口。故障是偶发的话抓包要在故障发生时同步进行事后补抓只能抓到正常流量。我一般会提前在关键节点部署 tcpdump循环抓包保留最后几百兆等故障发生再分析。企业环境用tcpdump -G 300 -W 288 -w /data/capture/$(hostname).pcap每 5 分钟一个文件滚动保留 24 小时等故障复现后提取对应时间段的文件效率比临时抓包高得多。日志时间对齐也是关键。分布式环境里各机器时间不一致排查时第一件事就是ntpdate或chronyc makestep校准时间否则你对比两个节点日志时会把因果搞颠倒。监控数据最好有统一的采集端Prometheus 的数据是带时间戳的对齐没有问题但手撸的日志就没有这种保证真正排查跨节点故障时时间不同步会让你浪费大量精力。3.3 一个端到端的排障案例复盘用一个实际排障过程把这些思路串起来。某天 10:30 左右某应用反馈接口超时率飙升用户访问页面转圈。监控显示 Web 服务器 CPU 不高但 error 日志开始出现大量 timeout负载均衡的健康检查也偶尔失败。第一步按三步法先做基础探测。在客户端机器pingWeb 服务器 IP丢包率是 0链路层没有明显问题telnet到 Web 端口秒开传输层也正常。此时网络层和传输层的“通与不通”都是通的问题锁定在应用层或后端依赖。第二步查 Web 日志。nginx error log 里看到大量upstream timed out说明 Web 服务器能收到请求但后端应用没有及时响应。进一步筛选 access log发现超时的请求全部集中在某个支付回调接口上。第三步查应用日志。这个支付回调接口的逻辑是先查用户账户、再调用外部支付平台确认状态、最后更新订单。日志显示大量线程阻塞在外部支付平台 HTTP 调用上等外部响应超时了线程池被占满后续所有接口全部排队等待。根因找到了是外部支付平台侧抖动导致应用线程池被慢调用占满。我们的系统用 Java 编写默认线程池队列大小配置过小且外部调用没有设置整体超时只有读超时排队耗尽了所有线程。网络层面没有任何异常。这个案例的启发是三层判断只花了不到五分钟后面大部分时间花在追踪应用调用链路。如果一开始不按三步法走直接跑去查防火墙和安全组方向就完全错了。排查链路问题永远是从客户端到服务端逐层推进不要跳不要猜。4. 常见问题速查与独家经验4.1 从现象到疑似原因的快速对照表把多年遇到的网络相关故障整理成一张速查表排障时对照使用比凭经验猜靠谱得多。现象可能原因第一步确认方式ping 不通路由/链路/防火墙traceroute -n看断在哪一跳ping 通但端口不通服务未监听、防火墙、安全组ss -lntp iptables端口能连但请求超时accept 队列满、应用线程阻塞ss -lnt看 Recv-Q偶发连接失败SYN 丢包、连接池耗尽tcpdump 看重传HTTP 502后端进程崩溃、后端未监听nginx error.logHTTP 504后端处理超时、慢依赖access log 的 upstream_timeDNS 解析慢resolver 配置问题、缓存污染dig time1 tries1证书报错证书过期、证书链不全、域名不匹配openssl s_client数据库连接报错连接泄漏、max_connections 满show processlist偶发大面积超时外部依赖抖动、线程池占满应用日志里阻塞调用链这张表不能替代排障流程它的价值是让你在定位过程中有一个方向性的参考。真正动手时还是要回到三步法层层确认避免被表面现象带偏。4.2 经常被忽略的细节排障时有一些细节特别容易被忽略但它们往往是最后一根救命稻草。第一个是 IPv6 和 IPv4 的差异。很多服务监听在::上此时同时支持 v4 和 v6但如果你只监听了[::]:8080而客户端通过 IPv4 地址访问时可能走不通或者/etc/gai.conf的优先级配置导致客户端优先尝试 IPv6 但 IPv6 路由不通。排查时用ss -lnt | grep 8080看监听地址curl -4和curl -6分别测试基本能定位。第二个是 MTU 黑洞。前面提到过路径上某个路由器 MTU 小于 9000 且静默丢弃大包时表现为大包不通、小包正常TCP 因为有 MSS 协商通常能自动规避但 UDP 和 ICMP 不行。确认方法还是ping -M do -s 1472和更大的包对比。第三个是 TCP keepalive 配置。系统默认tcp_keepalive_time是 7200 秒很多负载均衡和防火墙会在空闲连接超过 5 分钟时主动断开。如果应用依赖长连接且没有设置 keepalive就会出现“一段时间不用就断开用的时候报错”的现象。解决办法是调小 keepalive 时间或应用层加心跳。第四个是内核参数与业务场景不匹配。前面提到的somaxconn、tcp_max_syn_backlog、tcp_fin_timeout、tcp_tw_reuse都会影响服务端承受连接压力的能力。服务器默认配置往往适合通用场景高并发业务需要主动调优。调优时注意记录原始值用 sysctl 生效后一定要确认业务回归稳定再固化到/etc/sysctl.d/。第五个是系统时间。所有日志、证书校验、Kerberos 认证都依赖时间同步时间漂移会导致 TLS 证书验证失败、票据认证失败、日志无法关联。基础环境里配置 chrony 或 systemd-timesyncd不是可选项而是必选项。第六个是连接数限制。系统级ulimit -n和应用进程自己的连接数限制完全不同。排查时用ss -s看系统总连接数用/proc/pid/fd | wc -l看进程打开的文件描述符经常发现端口都正常但进程因为 fd 耗尽而无法接受新连接表现就是端口能连但应用不响应。4.3 我的实操心得这篇文章写到这里其实已经把三步法的传输/应用层部分全部覆盖了。最后分享几个我自己的使用习惯。第一个习惯是任何排障都从“最小客户端到服务器路径”开始。也就是你在离目标最近的地方先测比如直接在生产机本地 curl 自己通了再往外扩展。每一步往外扩就是在缩小问题范围这个过程比任何高级工具都可靠。第二个习惯是不要忽视重启这一个动作。很多老手不喜欢重启因为重启会掩盖现场。但如果你已经收集完该收集的日志和状态重启服务或重启容器是成本最低的快速恢复手段。真正的问题留到事后通过日志慢慢分析。我见过太多团队在生产环境上为了“保留现场”而让故障持续几个小时这其实是更严重的失误。第三个习惯是培养“分层验证”的肌肉记忆。出了问题脑子里立刻浮现三层判断每一层都给出明确结论后再决定要不要深层调查。不要因为“看起来像网络问题”就一头扎进抓包里很多所谓网络问题最终都是应用层日志可以一秒定位的。反过来也一样应用层报错也别急着改代码先用 curl 确认传输层通不通不少时候只是负载均衡把流量打到了一个已下线的节点。第四个习惯是建立知识库。每次排完故障哪怕只是很小的一个问题把现象、排查路径、根因、修复方式记下来按层分类。积累半年后你会发现大部分故障都能在知识库里找到影子排查时间从小时级降到分钟级。我给团队定的规矩是“故障不止于恢复必须沉淀一条记录”长期收益远超所花时间。4.4 最后再补充一个网络工具组合有读者让我推荐常用命令组合我把每天都会用到的排障命令贴出来这也是三步法的浓缩版# 第一步链路/网络层 ping -c 4 10.0.0.5 traceroute -n -w 1 10.0.0.5 ip route get 10.0.0.5 # 第二步主机与传输层 ss -s ss -lntp | grep -E :80|:443|:3306 iptables -L -n | grep -E DROP|REJECT ss -ant | awk {print $1} | sort | uniq -c # 第三步应用层 curl -v -m 10 http://10.0.0.5/health dig short service.example.com openssl s_client -connect service.example.com:443 -servername service.example.com /dev/null 2/dev/null | openssl x509 -noout -dates这套组合覆盖了从网络到应用的全链路按顺序执行能解决绝大多数连接类问题。每次执行时把输出结果保存下来方便和故障恢复后的状态对比。网络排障没有银弹就是靠逻辑和工具逐层推进熟练掌握这套方法后大部分故障都可以在十几分钟内定位到根因。