ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从浏览器到后台:一次HTTP请求的完整链路排查与加固

从浏览器到后台:一次HTTP请求的完整链路排查与加固 干过几年Web开发的兄弟应该都有过这种经历用户在浏览器里输了个网址回车页面瞬间打开你心里暗爽可偶尔某个深夜浏览器一直转圈圈你打开后台日志一看服务端连个请求的影子都没有。浏览器明明发出了请求这流量到底走哪去了从浏览器到后台服务这条链路远不是“点击一下”那么直白——中间要经过DNS解析、TCP连接、TLS握手、CDN调度、四层/七层负载均衡、反向代理、微服务网关、服务注册发现……任何一环出了问题流量就断了。这个问题不光前端要懂后端、运维、SRE都会遇到。今天我把自己这几年排过的坑、验证过的手段整理出来尽量用大白话把整条链路讲透并给你一套可以直接上手的排查和加固方案。1. 链路拆解一个浏览器请求要闯哪几道关1.1 从URL到IPDNS是第零公里用户在地址栏输入域名第一步不是连接服务器而是先问DNS“这个域名对应的IP是多少”这个过程看似简单实际坑特别多。浏览器的DNS缓存、操作系统的hosts文件、本地DNS服务器、运营商DNS、权威DNS每一层都可能命中缓存也可能全部失效。如果解析超时或返回错误IP整个请求就会在“第零公里”趴窝。比如我曾经遇到过用户反馈“访问时好时坏”最后发现是域名解析到了旧的机房IPCDN切换后TTL没调短运营商DNS缓存了旧记录导致部分用户走到一个已经下线的入口。所以日常监控不能只看后端存活还要盯DNS解析质量。建议至少每周做一次多云DNS解析检测记录不同地区、不同运营商返回的IP是否一致时延是否在正常范围。1.2 连接层面的三次握手与TLS协商拿到IP之后浏览器要跟目标服务器建立TCP连接经典的三次握手SYN、SYN-ACK、ACK。如果中间有防火墙、安全组拦截握手就会卡住浏览器表现就是一直转圈直到超时。很多新手排查时只看到“连接超时”却不知道要去看安全组规则、防火墙策略、iptables规则。之后如果是HTTPS还要加一轮TLS握手会多出证书交换、密钥协商几个来回。这时候SNIServer Name Indication也值得注意——如果一个IP上挂了多个域名TLS握手时就需要通过SNI告诉服务端访问的是哪个域名配置错了证书就会报警告甚至握手失败。我在帮朋友查一个“偶尔打不开”的问题时就是发现负载均衡监听的是同一个443端口但证书只绑定了一个域名另一个域名走了默认证书导致部分浏览器直接断开。1.3 应用层的“搭桥”从入口到业务服务TCP和TLS都通了HTTP请求才算真正送到“后台”。但这里的后台往往不是一台服务器而是Nginx、网关、微服务集群。流量要先到达一个统一的入口比如云负载均衡或Nginx再由它转发给后端的业务服务。如果后端是微服务架构入口还需要根据路径、Header把请求路由到不同的服务模块这就涉及网关路由表、服务注册中心。我见过不少团队把“流量到达Nginx”和“流量到达业务服务”混为一谈。Nginx返回200不代表业务服务收到了请求也可能是Nginx自己的缓存或默认页面。排查的时候一定要有“分段确认”的思路DNS通了没、TCP通了没、入口收到没、业务服务收到没逐段定位。拿我自己的习惯来说只要用户报障我会第一时间把一个测试请求从浏览器、curl、入口日志、业务日志四个视角各打一遍看看到底是哪一段断了。1.4 链路设计的核心每一跳都要有“兜底”看到这里你会发现整条链路其实是一连串转发。为了保证流量顺利触达后台每一跳都必须考虑三件事怎么让请求知道下一站在哪寻址、怎么快速稳定地转发传输、以及当下一站挂了怎么办容错。DNS的TTL、负载均衡的健康检查、网关的超时重试都是这三个问题的具体答案。理解了这一点你再看各种组件就不会觉得它们只是“中间商”。而且这条链路里还有一个隐藏问题反向代理和后端之间、网关和服务之间往往存在多个内网网段。如果安全组只开放了外网入口的端口而忘了放行内网转发流量就会出“浏览器能连上入口但入口连不上后端”的诡异故障。我在云上踩过这个坑那一次排查了整整半天最后发现是后端服务所在的安全组只放行了公网入方向内网源IP的访问全被丢掉了。从那以后我每次部署架构都会画一张端口/网段关系表逐个确认放行策略。2. 核心细节保证触达的七个关键点2.1 DNS层面该盯TTL和高可用DNS是第一跳也是容易被忽略的一跳。上线新服务时我建议域名解析用云解析或自建双机并给A记录设置合理的TTL——一般生产环境可以设300秒到600秒切换时临时改成60秒迁移完成后再调回来。同时要用多个运营商DNS轮询测试确保解析结果是正确的、时延是可控的。TTL设得太长流量迁移时老用户还往旧IP上撞设得太短则会让DNS服务器承受更大压力。我习惯的做法是日常600秒运维操作窗口前30分钟改成60秒操作完成后保持24小时再调回600秒。这套方法论虽然简单但能显著减少“切完DNS后还有用户访问旧节点”的投诉。2.2 转发层四层还是七层差别很大到了入口你首先要决定用四层传输层还是七层应用层负载均衡。四层L4只看IP和端口性能高适合长连接、大流量但不理解HTTP路径七层L7可以看URL、Header、Cookie做更细粒度的路由和灰度发布但TLS终结和HTTP解析会消耗CPU。实际场景中绝大多数Web业务走七层因为需要按路径转发到不同服务如果只是数据库或消息队列的内部调用用四层更合适。四层和七层在故障表现上也有明显差异四层转发如果后端宕机只能靠TCP级别的健康检测剔除实例表现是连接拒绝或超时七层转发可以对HTTP健康检查比如请求 /healthz 返回200才算存活能更早发现应用层“假死”。如果你的服务有健康检查接口一定要把它配到七层负载均衡上不要依赖端口探测。对比维度四层负载均衡七层负载均衡转发依据IP、端口URL、Header、Cookie、HTTP方法性能高转发快相对低有解析开销适用场景内部服务、长连接、实时通信Web业务、网关路由、灰度发布健康检查TCP连接检测HTTP路径检测更精细TLS终结一般不支持支持可统一管理证书2.3 反向代理配置别忽略健康检查Nginx是七层最常见的落地工具但很多配置是照抄的健康检查没做好。比如upstream里的server挂了Nginx默认还会把请求转发过去吗如果不配置max_fails和fail_timeout或者配得不合理就会导致一部分请求打到亚健康实例上。我习惯在每个upstream里加上upstream backend { server 10.0.0.11:8080 max_fails3 fail_timeout10s; server 10.0.0.12:8080 max_fails3 fail_timeout10s; keepalive 32; } server { location /api/ { proxy_pass http://backend; proxy_connect_timeout 3s; proxy_read_timeout 10s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Request-Id $request_id; } }这里keepalive 32是让Nginx和后端之间复用连接避免每次请求都重新三次握手。X-Request-Id特别重要它能在前端、Nginx、后端日志之间串联同一条请求排查慢接口时“顺着traceId查”会快很多。max_fails3代表连续失败3次就摘除实例fail_timeout10s表示10秒后重新试探这两个参数组合下来后端挂掉后最多十几秒流量就不会再打过去。2.4 网关层路由、限流、鉴权不能少如果应用做了微服务拆分前端请求往往先到网关如Spring Cloud Gateway、Kong、APISIX再由网关转发到具体的服务。网关的价值在于统一处理路由、鉴权、限流、灰度。限流尤其重要因为如果某个服务被突发流量打挂整个链路都通不了。网关层的超时要比业务超时短一些宁可快速失败也不要无限等待拖垮连接池。我自己做过一次压测峰值QPS到3000时业务服务其实还能扛但网关线程池先被打满了原因是每个请求等待下游响应的超时设置是30秒大量慢请求把线程占住不释放。后来把网关超时改成3秒并加上令牌桶限流系统一下子稳了——用户看到的是少量快速429而不是整体连接全部卡死。这里面有个设计原则进入系统的流量要“快进快出”不能把资源浪费在等一个注定失败的请求上。2.5 服务发现注册中心必须健康微服务环境下服务的IP和端口是动态变化的。网关或调用方需要从注册中心如Nacos、Consul拉取最新实例列表。如果注册中心里的心跳过期实例没被及时剔除流量就会转发到一个已经不存在的Pod或容器。所以要给服务配置正确的健康检查接口并调小心跳超时时间避免把“假活”的服务当成可用。我看到过一个典型案例一个服务实例因为内存泄漏进入了假死状态端口还在监听但线程池已经阻塞。注册中心一直认为它健康网关持续把流量分给它导致该实例的日志里出现大量超时。后来我们给每个微服务加了一个“业务健康检查”不是只检查端口而是检查一个轻量接口能否在2秒内返回注册中心每10秒探测一次连续3次失败就摘除节点。从那之后类似问题基本能自愈。2.6 超时和重试为了避免雪崩请求每经过一跳都有超时和重试配置。但重试不是越多越好如果后端已经过载盲目重试会放大流量造成雪崩。一般情况下入口到网关的重试可以关掉或设成1次业务内部调用可以结合幂等设计做1次重试。超时设置要分层Nginx→网关一般2-3秒网关→业务服务3秒业务服务内部调用视具体场景5-10秒。注意内层超时必须比外层短否则外层的重试和内层的超时叠加很容易拖垮线程池。我知道很多开发出于“想提高成功率”的动机把重试次数调到3次以上。但重试有个前提第一次请求可能已经生效了。比如扣款接口如果没做幂等重试就会导致用户被扣两次钱。所以我的建议是重试次数默认设0或1只有明确的读接口或带有幂等号的写接口才值得多试一次。2.7 连接池和会话保持浏览器到入口的连接、入口到后端的连接、后端到数据库的连接每段连接都建议配置连接池。HTTP连接池如Java的HttpClient、Go的Transport可以复用底层连接显著降低握手开销。对有状态服务比如需要Session或者需要将同一个用户粘到同一台机器时要开启会话保持sticky session否则用户请求被分发到不同实例登录态就丢了。但会话保持也可能导致负载不均所以现在很多系统都倾向于用无状态JWT Redis共享Session让任何一台实例都能处理请求。连接池的参数也值得细调比如Java的HttpClient连接池最大连接数默认值偏小高并发时会出现“等待获取连接”的瓶颈。我在生产环境一般把最大连接数设为200每个路由的maxPerRoute设为100连接空闲回收时间至少60秒避免反复建连。数据库连接池更要注意连接池上限设得太大数据库扛不住设得太小高峰期排队。这个没有固定值要根据压测结果调。3. 实测从一次线上故障看完整排查过程3.1 浏览器开发者工具先定位“卡在哪一跳”当用户反馈“页面打不开”第一步不是去服务器翻日志而是先在浏览器里按F12打开Network面板看具体请求的Status、Time、Size。如果状态码是200但页面白屏那是前端渲染问题如果一直Pending大概率是TCP/TLS层卡住了如果是504说明网关或Nginx连不上上游如果是502说明上游连接被拒绝。我一般还会右键请求把Request Headers里的Host、User-Agent、Cookie记下来方便后端定位。浏览器开发者工具还能看DNS解析时间。Chrome的Network面板里Timing中的“Stalled”和“DNS Lookup”两个指标很有参考价值。如果DNS Lookup时间突然变长说明域名解析异常可以对比不同浏览器和不同网络环境下的表现再判断是用户侧网络问题还是我们的DNS配置问题。3.2 用curl验证完整链路浏览器里看到的信息还不够还需要在命令行里模拟。我用这几条命令# 看DNS解析 dig example.com short # 看TCPTLS握手时间 curl -v -o /dev/null -w HTTP状态码:%{http_code} DNS时间:%{time_namelookup} 连接时间:%{time_connect} TLS握手:%{time_appconnect} 总时间:%{time_total}\n https://example.com/api/health # 从入口到后端验证 curl -H Host: example.com http://10.0.0.10:80/api/health -vcurl的这几个时间值很有用如果time_namelookup很长是DNS问题如果time_connect很长是网络或防火墙问题如果time_appconnect很长是TLS问题。后面那个带Host的curl主要用于“绕过DNS在接入层本地验证”——比如在外面访问不了但你的机器能连内网直接用内网IP和Host头测试就能确定问题出在外网入口还是内部转发。3.3 抓包确认到底是SYN丢了还是HTTP没回如果curl显示连接卡住我会上对端机器用tcpdump看一眼tcpdump -i eth0 -nn host 10.0.0.20 and tcp port 443正常能看到三次握手包如果只看到SYN没有SYN-ACK说明包被丢了需要查安全组和防火墙。如果握手正常但没有HTTP响应说明HTTP层或应用层卡住。这里顺便说一句别一上来就wireshark图形界面命令行抓包后导出再分析更快。tcpdump抓到的包可以先存成pcap文件再拖到本地用wireshark做可视化分析。分析时要关注几个过滤词tcp.flags.syn1、tcp.flags.reset1、http.response.code可以快速筛出关键帧。还有一个我吃过亏的细节如果抓包看到TCP有大量重传可能是MTU问题或丢包。这种情况下要检查网卡的MTU设置、链路质量以及是否有人在中间设备上做了流量整形。比如某些云厂商的IPS/安全设备会对过大包做分片或丢弃导致大请求体传不过去小请求正常。3.4 从后端日志反向确认最后一步是去后端服务查访问日志。一个合格的运维体系必须在接入层、网关层、业务服务各打一条带traceId的记录。如果没有traceId体系至少要在业务日志里记录来源IP、User-Agent、请求路径方便和前端、接入层的日志对起来。排查慢请求时后端要分阶段打点接收请求时间、开始执行时间、执行完成时间、响应写回时间这样才知道时间耗在哪一段。我分享一个实际的定位过程有一次用户反馈某接口偶发5秒超时。从浏览器看请求发出去后等待了4.8秒才返回504。用curl压测10次复现了3次。去Nginx日志看每个请求都被转发了但部分请求的上游响应时间超过5秒。再去业务日志看发现耗时主要卡在数据库查询上——某条SQL走了错误的索引。加索引之后接口从500ms降到30ms。这个例子说明浏览器、Nginx、业务日志三层对得上才能快速揪出真正的瓶颈。4. 常见问题速查浏览器到后台的典型故障4.1 故障现象与排查方向现象可能的环节优先排查浏览器一直转圈提示连接超时DNS、TCP、防火墙dig解析、ping/curl看连接、安全组规则页面返回502 Bad Gateway入口到后端后端服务是否存活、健康检查、端口监听页面返回504 Gateway Timeout网关/Nginx到上游上游响应是否太慢、超时配置是否过短静态资源能打开接口全挂网关路由、服务注册网关路由表、注册中心实例列表有时能开有时打不开负载均衡、DNS轮询健康检查配置、多实例负载、DNS缓存登录后跳转又变未登录会话保持、Cookie域Session共享、Cookie Domain配置浏览器报跨域CORS错误服务端响应头缺失检查Access-Control-Allow-Origin配置4.2 给了重试还是雪崩谈谈我的教训我曾经处理过一起事故上游数据库慢查询导致业务线程全部阻塞Nginx那边的健康检查请求也超时然后Nginx开始向下游返回502。下游服务看到502后自动重试结果把本来已经过载的上游打到完全没响应。后来我们把所有下游重试都改成最多1次并加了熔断。现在我再看到有人把重试次数调到3次以上就会提醒他重试的前提是下游故障是临时的、幂等的如果没有这两个前提重试就是灾难。熔断这个东西也值得提一下。它和限流、超时是配套的当某个下游错误率达到阈值网关直接快速失败不再转发请求给下游喘息的机会。熔断器打开后经过一段时间窗口再放少量流量试探成功就关闭熔断失败则继续开启。这套机制能有效防止“一倒倒一片”是保证流量触达的最后一层保护。4.3 一些细小但关键的经验先说说证书过期。我遇到不止一次HTTPS证书没监控浏览器直接提示“您的连接不是私密连接”用户根本走不到后台。这个一定要纳入监控而且监控要提前30天告警别等浏览器报错。证书自动化续期工具也很成熟比如acme.sh配crontab基本能实现无人值守。再说跨域。很多前端调试时会遇到CORS报错这时候请求其实已经到达了服务器只是浏览器因为跨域策略拦截了响应。这不属于“没触达”但容易误导排查。你需要在服务端正确配置CORS头并且区分“服务端没收到”和“浏览器没展示”。我们在开发环境一般让网关统一加Access-Control-Allow-Origin生产环境则按域名白名单控制。最后说CDN缓存。如果域名接了CDN你会发现静态资源请求到不了源站因为它们被CDN节点直接响应了。这不算链路故障但对于强缓存策略的接口比如某些场景下页面HTML可能会导致用户看到旧内容。排查“为什么源站没收到请求”时要先把CDN层的影响排除掉比如用curl -H Cache-Control: no-cache去源站验证看是否真的连不上。我个人在实际排查中养成的习惯是先在浏览器按F12再看网关访问日志再去看业务日志最后才动手改配置。这条链路每多一层就多一分不确定性所以关键是每一层都要留可观测的日志和指标。只要你能说清请求走到了哪一步问题就已经解决一半了。另外还有一个小技巧重要业务的上线前先做一次全链路演练用脚本模拟从DNS解析、入口转发、网关路由、服务调用的完整过程确保每个环节的监控告警都有效。网络问题永远防不胜防但把每一跳的可观测性补齐之后流量能不能顺利触达后台就不再是个“玄学”问题了。
返回列表