上一篇解决容量、inode 和日志生命周期,本篇转向请求链路。网络故障最浪费时间的做法是无目的 ping;高效排查应从本机配置开始,沿 DNS、路由、传输连接、应用协议和 TLS 分层缩小范围。
一、痛点:“超时”不是根因
用户看到超时,可能是域名没有解析、路由走错接口、防火墙丢包、服务只监听回环地址、连接队列满、TLS 握手失败,或应用本身迟迟不响应。ping使用 ICMP,很多环境会限速或禁用它;ping 失败不代表 TCP 443 不通,ping 成功也不代表 HTTP 正常。
第一步记录五元组:源地址、源端口、目标地址、目标端口和协议,并记录发生时间。没有时间戳就难以和服务日志、流日志及抓包对齐。接着判断影响范围:单个客户端、单个机房、单个地址族还是全部请求。比较“正常样本”和“异常样本”通常比盯着异常机器更快。
二、原理:从名字到字节逐层验证
DNS 返回地址,不保证地址可达。getent ahosts使用系统名称服务配置,最接近应用行为;dig适合直接查询 DNS 细节,两者结果不同可能来自/etc/hosts、缓存或 NSS。还要分别测试 A 与 AAAA,双栈环境常出现 IPv6 路径坏而应用优先选择 IPv6。
路由由目标前缀、策略规则和源地址共同决定。ip route get TARGET比打印整张路由表更直接,它显示内核实际选择的下一跳、设备和源 IP。到达服务器后,ss -lntp确认监听地址:127.0.0.1:8080只能本机访问,0.0.0.0:8080接收所有 IPv4 接口。
TCP 连接成功说明三次握手完成,不说明 TLS 和 HTTP 正常。curl -v能拆出解析、连接、握手、首字节阶段;openssl s_client可检查证书链和 SNI。抓包是后手而非第一步,因为它需要权限、可能捕获敏感数据,也只展示观测点看到的流量。
三、实现:一键收集分层证据
下面脚本接收主机和端口,设置明确超时,依次检查解析、路由、TCP 和 HTTPS。它不修改系统,不依赖前文产物,适合把完整输出附到故障记录。若目标是 HTTP,应把最后的 URL 改为对应协议。
#!/usr/bin/env bashset-uopipefail[[$#-eq2]]||{echo"用法:$0HOST PORT">&2;exit2;}host="$1"port="$2"[["$port"=~^[0-9]+$]]&&((port>0&&port<65536))||exit2echo"time=$(date-u+%FT%TZ)host=$(hostname)target=$host:$port"echo'--- addresses'getent ahosts"$host"||{echo'DNS/NSS 解析失败'>&2;exit10;}mapfile-taddresses<<(getent ahosts"$host"|awk'$2=="STREAM" {print $1}'|sort-u)foraddressin"${addresses[@]}";doecho"--- route$address"iproute get"$address"2>&1||truedoneecho'--- local listeners'ss-lntp2>/dev/null|head-n20echo'--- tcp probe'iftimeout5bash-c'exec 3<>"/dev/tcp/$1/$2"'_"$host""$port";thenecho'tcp=connected'elseecho'tcp=failed'>&2exit20fiecho'--- https timing'curl--silent--show-error--output/dev/null\--connect-timeout5--max-time15\--write-out'code=%{http_code} dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}\n'\"https://$host:$port/"运行输出:
addresses=203.0.113.10 tcp_connect=ok host=api.example.net port=443 code=200 dns=0.012 connect=0.028 tls=0.061 first_byte=0.104 total=0.105需要验证本机监听时,可独立启动临时 HTTP 服务并在 trap 中回收。此例使用socat,不绑定公网接口,只监听回环地址;它验证的是 TCP 与最小 HTTP 响应,不涉及生产服务。
#!/usr/bin/env bashset-euopipefailcommand-vsocat>/dev/null||{echo'请先安装 socat'>&2;exit127;}port=18080tmp="$(mktemp-d)"cleanup(){[[-n"${server_pid:-}"]]&&kill"$server_pid"2>/dev/null||truerm-rf--"$tmp"}trapcleanup EXIT INTTERMcat>"$tmp/respond"<<'EOF' #!/usr/bin/env bash printf 'HTTP/1.1 200 OK\r\nContent-Length: 3\r\nConnection: close\r\n\r\nok\n' EOFchmod0755"$tmp/respond"socat TCP-LISTEN:$port,bind=127.0.0.1,reuseaddr,fork EXEC:"$tmp/respond"&server_pid=$!forattemptin{1..20};doss-lnt"sport = :$port"|grep-qLISTEN&&breaksleep0.1donecurl--fail--silent--show-error"http://127.0.0.1:$port/"ss-lntp"sport = :$port"运行输出:
ok State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 5 127.0.0.1:18080 0.0.0.0:*四、踩坑:抓包位置决定结论
客户端发出 SYN 而服务端抓不到,故障位于两者之间或抓错接口;服务端收到 SYN 并回复 SYN-ACK,客户端却收不到,应检查返回路由和中间策略;三次握手完成后立即 RST,常见于应用关闭、协议不匹配或代理行为。使用tcpdump -nn禁止反向解析,过滤具体主机和端口,并控制包数与文件权限。
容器和云环境还有虚拟网卡、NAT、负载均衡健康检查与安全组。主机上的源地址可能已被代理改写,连接跟踪表也可能成为瓶颈。先画清实际路径,再选择抓包点;不要看到某个节点正常就推断整条链路正常。
五、验证:保留可比较的时间数据
将 curl 各阶段时间、解析地址、路由结果和服务端请求 ID 放进同一事件。DNS 时间高查解析器,connect 高查路由和防火墙,TLS 高查证书与 CPU,首字节高则更可能是应用或后端。重复测试要限制频率,避免探测本身放大故障。
网络路径清楚后,下一篇进入主机内部:用 CPU、内存、I/O、负载和压力指标判断真正瓶颈,并解释为什么“load 很高”不一定是 CPU 不够。
参考来源
- ip-route 手册
- ss 手册
- curl:Write-out variables
- tcpdump Manual
👍 觉得有用就点个赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。
🚀 本文属于《Linux 服务器运维实战》系列,持续更新,关注不迷路。
📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。