很多朋友第一次听到“从输入淘宝网址到页面展示中间发生了什么”这个面试必考题时,第一反应都是:这不就是输个网址按回车吗?但真正把这一整条链路走一遍,你会发现它涉及 DNS 解析、TCP 连接、TLS 加密、HTTP 请求、CDN 调度、负载均衡、服务端渲染、浏览器解析渲染等一大堆环节。这篇文章我尽量用最通俗的方式,把这条链路从头到尾串一遍,不堆术语,但该有的细节一个都不少。不管你是刚入门的前端、后端,还是整天跟网络打交道的运维,耐心看完,下次再聊到“打开一个网页背后发生了什么”,你心里就有完整的图谱了。这里我以大家最熟悉的“点开淘宝”这类头部电商平台为例,因为它的链路足够复杂、足够典型。日常我们打开本地开发环境,可能测不出问题,但一旦到了这种千万级并发的场景,每一个环节都可能成为瓶颈,而理解这条链路,本质上是理解互联网应用架构的起点。1. 从网址到 IP:DNS 解析为什么要走这么多层1.1 为什么不能用 IP 直接访问你在浏览器地址栏输入一串类似www.taobao.com的域名时,第一件发生的事情并不是网络请求,而是一个本地“查表”操作。计算机和计算机之间通信,靠的是 IP 地址,不是域名。域名是给人看的,IP 是给机器用的。这有点像你手机通讯录里存的是“老张”,但真正拨号时是拨“138xxxx”这个号码,通讯录只是帮你做了一次映射。那么问题来了,谁来帮我们把“老张”查成“138xxxx”?就是 DNS(域名系统)。浏览器第一步会去查这个域名对应的 IP 地址,这个查询是分层的,因为没有任何一台服务器能存下全互联网的域名映射关系,所以 DNS 本身是一棵分布式树。1.2 一个完整的递归查询过程以输入www.taobao.com为例,你本机拿到的 IP 地址,通常经过这样几步:第一步,查浏览器缓存。浏览器在本地会缓存最近访问过的域名解析结果,如果之前访问过这个站点,缓存还没过期,就直接用缓存里的 IP,连 DNS 服务器都不用找。第二步,查操作系统缓存。浏览器没命中,就发起系统调用去查本机的 hosts 文件和系统 DNS 缓存。这里有个容易踩的坑:很多开发者在自己电脑上改 hosts 做本地环境绑定,结果后来忘了删,线上环境被莫名其妙带到了测试服务器,排查起来非常痛苦。第三步,交给本地 DNS 服务器。操作系统拿到你配置的 DNS 服务器地址(通常是你路由器自动获取的运营商 DNS,或者公共 DNS),把域名发给它。这一步叫递归查询,因为本地 DNS 服务商会替你把整条链走完,最后返回一个结果给你。第四步,本地 DNS 服务器开始迭代查询。它会先向全球 13 个根 DNS 服务器中的一个发起请求,问它.com这个顶级域的服务器在哪;然后拿着返回的地址再去问.com服务器,taobao.com这个域的权威服务器在哪;最后去问taobao.com的权威 DNS,www这个主机名对应的记录是多少。一般情况下,这层权威服务器上会有 A 记录或 CNAME 记录。这里需要注意一个高频面试点:为什么有时候换了 DNS 之后,访问网站会变快或变慢?因为本地 DNS 服务商如果有智能调度能力,会给你返回一个离你最近的边缘节点 IP,这是 CDN 能加速的前提。你手动指定了某个较慢的公共 DNS,反而可能绕远了。1.3 缓存在这里扮演的角色整个 DNS 流程中,缓存无处不在。浏览器有浏览器缓存,操作系统有系统缓存,本地 DNS 服务商有区缓存,每一级都有 TTL(生存时间)。TTL 的长短直接影响了域名变更的生效速度。我印象很深的一次经历:某电商平台做了一次机房迁移,把所有域名的 DNS 记录从 A 切到了另一组 IP。运维提前把 TTL 调成了 60 秒,但还是有用户反馈访问不稳定,原因是部分浏览器和运营商缓存了旧 IP。后来加了一段兼容期,让旧 IP 继续转发了一段时间才彻底解决问题。这个案例告诉你,改 DNS 记录绝不能只看“权威服务器改好了”,要留足缓存退避期。所以,当你输入一个网址按下回车,第一道关卡就是 DNS,而这道关卡本身就涉及四五层缓存。理解了这层,后面就好办了。2. 建立连接:三次握手与 TLS 加密的互相配合2.1 TCP 三次握手,但服务器不止连一次拿到 IP 之后,浏览器开始发起 TCP 连接。TCP 是传输层协议,负责把数据可靠地从一个端传到另一个端。为了建立这个可靠通道,TCP 需要完成一次“三次握手”:客户端先发一个 SYN 包,表示“我想跟你建立连接”;服务器收到后回一个 SYN+ACK,表示“收到,我也准备好”;客户端再回一个 ACK,表示“双方确认”。这个过程看起来很简单,但实际在浏览器里,连接可不是只建一次。头部电商站点的页面资源动辄上百个请求,如果每个请求都重新建一次 TCP 连接,延迟会高到无法接受。所以现代浏览器和 HTTP 协议做了两件事:一是 HTTP/1.1 的 Keep-Alive,让同一连接被多个请求复用;二是 HTTP/2 的多路复用,允许同一个连接上并发传输多个请求。你在浏览器开发者工具里看到的“Connection ID”相同,就说明请求是在同一条连接上跑的。2.2 TLS 握手:安全是代价换来的光有 TCP 还不行,现在的网络环境必须加密。HTTPS 就是 HTTP + TLS,TLS 握手发生在 TCP 三次握手之后。这个过程需要:第一步,协商版本。客户端把自己的 TLS 版本、支持的加密套件列表发给服务器。第二步,证书验证。服务器返回自己的证书,客户端要验证证书的签发机构、域名、有效期。这一步一旦失败,浏览器会直接弹出“您的连接不是私密连接”的警告。这实际上是因为证书链不完整、证书过期,或者访问的域名和证书上的域名不匹配。第三步,密钥交换。客户端和服务器各自生成随机数,再通过非对称加密算法安全地交换出最终用来加密会话的对称密钥。很多人不理解为什么要搞这么复杂:直接用非对称加密不是更安全吗?因为非对称加密虽然安全,但性能开销远远高于对称加密。所以实际方案是“非对称加密握手,对称加密传输”,兼顾安全和性能。TLS 握手通常耗时 1 到 2 个 RTT(往返时间)。在移动网络环境下,这个成本不小。于是有了 TLS 1.3,把握手缩短到 1 个 RTT,甚至 0-RTT 恢复会话。我实测过某个电商首页,启用 TLS 1.3 后,首打开时间大概能快 100 毫秒左右,对全国用户基数来说,这个优化非常可观。2.3 连接层面的一个常见误区很多初学者以为“浏览器地址栏显示小锁就是绝对安全”,其实只代表传输过程中加密了。你访问的页面如果被植入了恶意的第三方脚本,小锁照样亮着。TLS 只能保护传输,保护不了你信任的页面本身。还有一个小细节:HTTPS 和 HSTS(HTTP 严格传输安全)的配合。很多人不知道,你输入taobao.com时,现代浏览器会先尝试直接走 HTTPS,而不是先走 HTTP 再 302 跳转。这是因为淘宝这类站点启用了 HSTS 预加载,把你的浏览器里预埋了一条“该域名强制 HTTPS”的规则。这个机制可以避免那个 302 跳转带来的额外请求,减少被劫持的风险。3. 请求真正发出去了:HTTP 报文里到底装了什么3.1 一个典型的 GET 请求是怎么拼出来的TCP 和 TLS 都就绪之后,浏览器开始组装 HTTP 请求。一个请求报文由请求行、请求头和请求体组成。对访问https://www.taobao.com/这种事来说,请求行基本是:GET / HTTP/2 Host: www.taobao.com User-Agent: Mozilla/5.0 ... Accept: text/html,application/xhtml+xml... Cookie: ...这一小段报文里有几个关键信息:Host告诉服务器你要访问哪个域名,服务器上可能同时跑着几百个站点,靠它来区分。User-Agent标识客户端类型,服务端会据此返回移动端页面、PC 页面或者给爬虫的简化页面。Cookie携带了登录态、推荐位参数、埋点参数等,电商网站的推荐算法很大程度上依赖这些信息。服务端收到请求后,返回的响应报文也有类似结构,状态行里有状态码,头部里有Content-Type、Cache-Control、Set-Cookie等。3.2 那个请求到底先到了哪里很多人画网络请求流程图,喜欢直接从浏览器画到应用服务器。但在真实的大型站点架构里,请求的路径会复杂得多。第一步是 CDN 边缘节点。像淘宝这种体量的电商平台,静态资源(图片、CSS、JS 文件)几乎全部通过 CDN 分发。CDN 的调度策略是依据你的本地 DNS 返回的节点 IP,把你导向一个物理上离你最近的边缘服务器。如果这张图片或脚本已经在边缘节点上有缓存,那请求到这里就结束了,根本不会打到源站。第二步是接入层和负载均衡。动态请求会通过四层负载均衡或七层负载均衡分发到后面的应用服务器集群。四层负载均衡工作在传输层,性能高,适合流量分发;七层负载均衡能解析 HTTP 头部,适合做路由、限流、灰度发布。第三步才是应用服务器。应用服务器处理业务逻辑、调用数据库、操作缓存。这里有个关键点:数据库通常不是直接连接,而是先查 Redis 这一类缓存,本地缓存没命中才查分布式缓存,再没命中才查数据库。这一套“多级缓存”机制,才是高并发场景能力建设的主战场。3.3 状态码里的隐性沟通HTTP 状态码是客户端和服务端之间的暗号。我给你列几个日常最容易碰到的场景:状态码含义典型场景200成功页面正常返回301永久重定向旧域名跳转新域名302临时重定向未登录用户跳到登录页304未修改浏览器缓存可用,服务器不需要重新传资源403禁止访问无权限,或者被防火墙拦截404未找到页面或接口不存在500服务器内部错误代码抛了未捕获异常502网关错误应用服务器挂了,网关无响应503服务不可用服务过载或维护中每次看到 504,可以重点考虑网关到应用服务器的链路;看到 499,通常是客户端主动断开等待,大查询、慢接口常出这个问题。排查线上问题时,状态码本身就是第一层线索。4. 页面从一片空白到完整展示,浏览器做了什么4.1 HTML 解析与 DOM 树构建服务器返回 HTML 文本后,浏览器开始解析。解析器会以流式的方式读 HTML,边读边构建 DOM 树。这非常重要,因为浏览器不是等全部 HTML 下载完才开始解析,尤其现代页面都是分块传输的,浏览器收到一部分就解析一部分。这也是为什么很多优化技巧强调“首屏 CSS 内联、JS 后置”——因为这些资源会阻塞解析。遇到<script>标签时,浏览器会停止解析 HTML,先下载并执行脚本。这就是脚本阻塞渲染的根本原因。现在有了async和defer两个属性,可以控制脚本异步加载。defer会等到 HTML 解析完再执行,async是下载完就执行,两者用途不同。4.2 构建 CSSOM,合成渲染树解析完 HTML 构建 DOM,同时解析 CSS 构建 CSSOM。DOM 和 CSSOM 合成渲染树,浏览器会遍历渲染树上的每个节点,计算它的精确位置和大小,这个过程叫 Layout(回流)。然后进入 Paint(绘制)阶段,把每个像素画到屏幕上。再做合成(Composite),把图层合并成最终页面。这里有一个很重要的概念:你打开开发者工具的 Performance 面板,看到的长条就是这些阶段的耗时分布。首屏慢通常不外乎几个原因:HTML 解析被同步脚本卡住、CSS 文件太大导致 CSSOM 构建慢、图片体积太大导致下载时间过长、布局阶段重排次数太多。访问一个电商首页时,你可能会看到一个有意思的现象:先出来框架,再出图片,最后文案和详情模块慢慢填充。这是因为页面采取了“骨架屏 + 懒加载 + 分片加载”策略,通过 JS 控制内容逐步渲染,视觉上让用户觉得“页面有反应”,把后端接口慢的体感降到最低。4.3 造出一个新页面要重新走完整链路吗如果用 SPA(单页应用)架构,比如 Vue 或 React 技术栈,那么整个页面切换通常是纯前端路由跳转,不再请求新的 HTML 文档。这样你按下“下一页”的时候,浏览器并不重新执行 DNS 解析、TCP 三次握手这些流程,而只是向服务端要数据。所以现在的电商页面实际上被拆成了两部分:一个主文档框架 + 一堆接口数据。主文档框架可能一天都不变,走 CDN 强缓存;接口数据按用户实时变化,走带鉴权的动态请求。理解这个区分,是以后做前端性能优化的基础。5. 线上慢问题排查:从链路视角定位瓶颈5.1 “慢”到底慢在哪一段平时我们遇到“页面打开慢”的问题,可以先别急着改代码,先做链路性能分析。我个人排查的顺序一般是这样:第一步,看瀑布图。浏览器开发者工具里的 Network 面板,每一行就是一个请求,右侧的瀑布图很直观。先看主文档请求本身的耗时,如果这个请求就花了 3 秒,基本可以确定是网络链路或服务端处理慢;如果主文档很快,但是某个 JS 文件花了很大一部分时间,问题就可能是资源体积过大、CDN 缓存命中率低,或者脚本阻塞了渲染。第二步,把请求耗时拆开看。瀑布图里每一段有不同颜色:有 DNS 查找、建立连接、TLS 握手、发送请求、等待服务器响应、下载内容。DNS 查找时间长,检查本地 DNS 配置;TLS 握手时间长,检查证书链和加密套件;等待服务器响应时间长,基本是后端接口问题,需要去看数据库慢查询、上游依赖耗时。第三步,对比不同节点。我在实际工作中排查过一次访问慢,用户反馈在某些地区很慢,但本地复现不了。后来分别在几个主要接入点做了定时采集,发现其中一个省份的运营商 DNS 对 CDN 调度返回了异常的边缘节点 IP,导致用户实际访问时跨了很长的物理链路。这个情况通过代码层面完全看不出问题,只有链路视角才能定位。5.2 用适合的工具拆链路排查链路的时候,有几个工具我很常用:浏览器开发者工具:最基础,看瀑布图、请求头、响应头、时间和 Timing 信息,全是免费可用。curl -w:在终端手动模拟请求,拿到 DNS 解析时间、连接时间、首字节时间等指标。curl -o /dev/null -s -w "dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n" https://www.taobao.com/这条命令可以让你快速把耗时拆解到不同环节,看看到底是哪段拖了后腿。在线拨测工具:适合做地域性对比,能从多个城市发起请求,拿到全国的延迟情况。如果某个地区明显异常,再针对性排查运营商和 CDN 调度。5.3 几个容易误导排查方向的隐藏坑最后分享几个查链路慢时会干扰判断的点,都属于“看着正常但实际不正常”的情况:第一个坑:缓存命中也计入耗时。有时候瀑布图显示某个资源很快,但其实是命中了浏览器本地缓存,没有走真实网络。排查线上问题时,最好打开开发者工具的“Disable cache”选项,再把 Network 面板的缓存状态从memory cache或disk cache看成真实请求。第二个坑:图片资源的懒加载会掩盖性能问题。很多电商页面的图片是滚动到可视区域才开始加载的,如果你只盯着 Network 面板看,会发现首屏明明只有少数几个请求,页面的 image 总大小也远低于预期。这不代表页面没问题,而是因为数据还没被真正加载。需要关注的是接口返回的图片地址列表和实际触发加载的时机。第三个坑:本地环境是 HTTPS,线上是 HTTP,或者反过来。HTTPS 有握手开销,HTTP 没有,两者在请求耗时上天然就有差距,但如果页面本身同时混合了 HTTPS 和 HTTP 资源,现代浏览器会自动把 HTTP 请求升级,这个升级过程也可能带来额外的重定向开销。第四个坑:DNS 预解析和预连接。很多站点在 HTML 里写了<link rel="dns-prefetch">或<link rel="preconnect">,浏览器会提前去解析某些域名或者预先建立连接,这会使得真实访问某些跨域资源时,瀑布图上显示的时间比实际网络耗时更长。因为资源加载可能发起得非常早,甚至在主文档解析之前就开始建连。6. 关于这条链路,我最想强调的两件事既然聊到了这里,我想说两个可以马上落地的认知。一个是,链路里每一层都在努力复用和缓存。DNS 有缓存,TCP 有连接复用,TLS 有会话恢复,HTTP 有缓存头,CDN 有边缘缓存,服务端有 Redis、多级缓存,浏览器本身还有 HTTP Cache、DNS Cache、预连接等等。所谓的性能优化,本质上是把每一层的缓存和最简化路径挖掘到极致。另一个是,链路是一个整体,任何局部优化都可能被其他环节抵消。比如你把后端接口从原来的 500 毫秒降到了 100 毫秒,但如果 CDN 缓存命中率太低、或者 DNS 解析跨地域严重,用户端首屏依然快不了多少,需要整体看。我自己排查过很多类似问题,最有成就感的不是某个优化点,而是对整套链路有了全局掌控感。从那以后,不管是前端反馈的加载慢、后端反馈的连接超时、运营反馈的某地区无法访问,只要给我请求的 URL 和一个抓到的 Timing 时间,基本都能往对应的链路环节去猜,然后快速验证,这比漫无目的地翻日志高效太多了。
很多朋友第一次听到“从输入淘宝网址到页面展示中间发生了什么”这个面试必考题时,第一反应都是:这不就是输个网址按回车吗?但真正把这一整条链路走一遍,你会发现它涉及 DNS 解析、TCP 连接、TLS 加密、HTTP 请求、CDN 调度、负载均衡、服务端渲染、浏览器解析渲染等一大堆环节。这篇文章我尽量用最通俗的方式,把这条链路从头到尾串一遍,不堆术语,但该有的细节一个都不少。不管你是刚入门的前端、后端,还是整天跟网络打交道的运维,耐心看完,下次再聊到“打开一个网页背后发生了什么”,你心里就有完整的图谱了。这里我以大家最熟悉的“点开淘宝”这类头部电商平台为例,因为它的链路足够复杂、足够典型。日常我们打开本地开发环境,可能测不出问题,但一旦到了这种千万级并发的场景,每一个环节都可能成为瓶颈,而理解这条链路,本质上是理解互联网应用架构的起点。1. 从网址到 IP:DNS 解析为什么要走这么多层1.1 为什么不能用 IP 直接访问你在浏览器地址栏输入一串类似www.taobao.com的域名时,第一件发生的事情并不是网络请求,而是一个本地“查表”操作。计算机和计算机之间通信,靠的是 IP 地址,不是域名。域名是给人看的,IP 是给机器用的。这有点像你手机通讯录里存的是“老张”,但真正拨号时是拨“138xxxx”这个号码,通讯录只是帮你做了一次映射。那么问题来了,谁来帮我们把“老张”查成“138xxxx”?就是 DNS(域名系统)。浏览器第一步会去查这个域名对应的 IP 地址,这个查询是分层的,因为没有任何一台服务器能存下全互联网的域名映射关系,所以 DNS 本身是一棵分布式树。1.2 一个完整的递归查询过程以输入www.taobao.com为例,你本机拿到的 IP 地址,通常经过这样几步:第一步,查浏览器缓存。浏览器在本地会缓存最近访问过的域名解析结果,如果之前访问过这个站点,缓存还没过期,就直接用缓存里的 IP,连 DNS 服务器都不用找。第二步,查操作系统缓存。浏览器没命中,就发起系统调用去查本机的 hosts 文件和系统 DNS 缓存。这里有个容易踩的坑:很多开发者在自己电脑上改 hosts 做本地环境绑定,结果后来忘了删,线上环境被莫名其妙带到了测试服务器,排查起来非常痛苦。第三步,交给本地 DNS 服务器。操作系统拿到你配置的 DNS 服务器地址(通常是你路由器自动获取的运营商 DNS,或者公共 DNS),把域名发给它。这一步叫递归查询,因为本地 DNS 服务商会替你把整条链走完,最后返回一个结果给你。第四步,本地 DNS 服务器开始迭代查询。它会先向全球 13 个根 DNS 服务器中的一个发起请求,问它.com这个顶级域的服务器在哪;然后拿着返回的地址再去问.com服务器,taobao.com这个域的权威服务器在哪;最后去问taobao.com的权威 DNS,www这个主机名对应的记录是多少。一般情况下,这层权威服务器上会有 A 记录或 CNAME 记录。这里需要注意一个高频面试点:为什么有时候换了 DNS 之后,访问网站会变快或变慢?因为本地 DNS 服务商如果有智能调度能力,会给你返回一个离你最近的边缘节点 IP,这是 CDN 能加速的前提。你手动指定了某个较慢的公共 DNS,反而可能绕远了。1.3 缓存在这里扮演的角色整个 DNS 流程中,缓存无处不在。浏览器有浏览器缓存,操作系统有系统缓存,本地 DNS 服务商有区缓存,每一级都有 TTL(生存时间)。TTL 的长短直接影响了域名变更的生效速度。我印象很深的一次经历:某电商平台做了一次机房迁移,把所有域名的 DNS 记录从 A 切到了另一组 IP。运维提前把 TTL 调成了 60 秒,但还是有用户反馈访问不稳定,原因是部分浏览器和运营商缓存了旧 IP。后来加了一段兼容期,让旧 IP 继续转发了一段时间才彻底解决问题。这个案例告诉你,改 DNS 记录绝不能只看“权威服务器改好了”,要留足缓存退避期。所以,当你输入一个网址按下回车,第一道关卡就是 DNS,而这道关卡本身就涉及四五层缓存。理解了这层,后面就好办了。2. 建立连接:三次握手与 TLS 加密的互相配合2.1 TCP 三次握手,但服务器不止连一次拿到 IP 之后,浏览器开始发起 TCP 连接。TCP 是传输层协议,负责把数据可靠地从一个端传到另一个端。为了建立这个可靠通道,TCP 需要完成一次“三次握手”:客户端先发一个 SYN 包,表示“我想跟你建立连接”;服务器收到后回一个 SYN+ACK,表示“收到,我也准备好”;客户端再回一个 ACK,表示“双方确认”。这个过程看起来很简单,但实际在浏览器里,连接可不是只建一次。头部电商站点的页面资源动辄上百个请求,如果每个请求都重新建一次 TCP 连接,延迟会高到无法接受。所以现代浏览器和 HTTP 协议做了两件事:一是 HTTP/1.1 的 Keep-Alive,让同一连接被多个请求复用;二是 HTTP/2 的多路复用,允许同一个连接上并发传输多个请求。你在浏览器开发者工具里看到的“Connection ID”相同,就说明请求是在同一条连接上跑的。2.2 TLS 握手:安全是代价换来的光有 TCP 还不行,现在的网络环境必须加密。HTTPS 就是 HTTP + TLS,TLS 握手发生在 TCP 三次握手之后。这个过程需要:第一步,协商版本。客户端把自己的 TLS 版本、支持的加密套件列表发给服务器。第二步,证书验证。服务器返回自己的证书,客户端要验证证书的签发机构、域名、有效期。这一步一旦失败,浏览器会直接弹出“您的连接不是私密连接”的警告。这实际上是因为证书链不完整、证书过期,或者访问的域名和证书上的域名不匹配。第三步,密钥交换。客户端和服务器各自生成随机数,再通过非对称加密算法安全地交换出最终用来加密会话的对称密钥。很多人不理解为什么要搞这么复杂:直接用非对称加密不是更安全吗?因为非对称加密虽然安全,但性能开销远远高于对称加密。所以实际方案是“非对称加密握手,对称加密传输”,兼顾安全和性能。TLS 握手通常耗时 1 到 2 个 RTT(往返时间)。在移动网络环境下,这个成本不小。于是有了 TLS 1.3,把握手缩短到 1 个 RTT,甚至 0-RTT 恢复会话。我实测过某个电商首页,启用 TLS 1.3 后,首打开时间大概能快 100 毫秒左右,对全国用户基数来说,这个优化非常可观。2.3 连接层面的一个常见误区很多初学者以为“浏览器地址栏显示小锁就是绝对安全”,其实只代表传输过程中加密了。你访问的页面如果被植入了恶意的第三方脚本,小锁照样亮着。TLS 只能保护传输,保护不了你信任的页面本身。还有一个小细节:HTTPS 和 HSTS(HTTP 严格传输安全)的配合。很多人不知道,你输入taobao.com时,现代浏览器会先尝试直接走 HTTPS,而不是先走 HTTP 再 302 跳转。这是因为淘宝这类站点启用了 HSTS 预加载,把你的浏览器里预埋了一条“该域名强制 HTTPS”的规则。这个机制可以避免那个 302 跳转带来的额外请求,减少被劫持的风险。3. 请求真正发出去了:HTTP 报文里到底装了什么3.1 一个典型的 GET 请求是怎么拼出来的TCP 和 TLS 都就绪之后,浏览器开始组装 HTTP 请求。一个请求报文由请求行、请求头和请求体组成。对访问https://www.taobao.com/这种事来说,请求行基本是:GET / HTTP/2 Host: www.taobao.com User-Agent: Mozilla/5.0 ... Accept: text/html,application/xhtml+xml... Cookie: ...这一小段报文里有几个关键信息:Host告诉服务器你要访问哪个域名,服务器上可能同时跑着几百个站点,靠它来区分。User-Agent标识客户端类型,服务端会据此返回移动端页面、PC 页面或者给爬虫的简化页面。Cookie携带了登录态、推荐位参数、埋点参数等,电商网站的推荐算法很大程度上依赖这些信息。服务端收到请求后,返回的响应报文也有类似结构,状态行里有状态码,头部里有Content-Type、Cache-Control、Set-Cookie等。3.2 那个请求到底先到了哪里很多人画网络请求流程图,喜欢直接从浏览器画到应用服务器。但在真实的大型站点架构里,请求的路径会复杂得多。第一步是 CDN 边缘节点。像淘宝这种体量的电商平台,静态资源(图片、CSS、JS 文件)几乎全部通过 CDN 分发。CDN 的调度策略是依据你的本地 DNS 返回的节点 IP,把你导向一个物理上离你最近的边缘服务器。如果这张图片或脚本已经在边缘节点上有缓存,那请求到这里就结束了,根本不会打到源站。第二步是接入层和负载均衡。动态请求会通过四层负载均衡或七层负载均衡分发到后面的应用服务器集群。四层负载均衡工作在传输层,性能高,适合流量分发;七层负载均衡能解析 HTTP 头部,适合做路由、限流、灰度发布。第三步才是应用服务器。应用服务器处理业务逻辑、调用数据库、操作缓存。这里有个关键点:数据库通常不是直接连接,而是先查 Redis 这一类缓存,本地缓存没命中才查分布式缓存,再没命中才查数据库。这一套“多级缓存”机制,才是高并发场景能力建设的主战场。3.3 状态码里的隐性沟通HTTP 状态码是客户端和服务端之间的暗号。我给你列几个日常最容易碰到的场景:状态码含义典型场景200成功页面正常返回301永久重定向旧域名跳转新域名302临时重定向未登录用户跳到登录页304未修改浏览器缓存可用,服务器不需要重新传资源403禁止访问无权限,或者被防火墙拦截404未找到页面或接口不存在500服务器内部错误代码抛了未捕获异常502网关错误应用服务器挂了,网关无响应503服务不可用服务过载或维护中每次看到 504,可以重点考虑网关到应用服务器的链路;看到 499,通常是客户端主动断开等待,大查询、慢接口常出这个问题。排查线上问题时,状态码本身就是第一层线索。4. 页面从一片空白到完整展示,浏览器做了什么4.1 HTML 解析与 DOM 树构建服务器返回 HTML 文本后,浏览器开始解析。解析器会以流式的方式读 HTML,边读边构建 DOM 树。这非常重要,因为浏览器不是等全部 HTML 下载完才开始解析,尤其现代页面都是分块传输的,浏览器收到一部分就解析一部分。这也是为什么很多优化技巧强调“首屏 CSS 内联、JS 后置”——因为这些资源会阻塞解析。遇到<script>标签时,浏览器会停止解析 HTML,先下载并执行脚本。这就是脚本阻塞渲染的根本原因。现在有了async和defer两个属性,可以控制脚本异步加载。defer会等到 HTML 解析完再执行,async是下载完就执行,两者用途不同。4.2 构建 CSSOM,合成渲染树解析完 HTML 构建 DOM,同时解析 CSS 构建 CSSOM。DOM 和 CSSOM 合成渲染树,浏览器会遍历渲染树上的每个节点,计算它的精确位置和大小,这个过程叫 Layout(回流)。然后进入 Paint(绘制)阶段,把每个像素画到屏幕上。再做合成(Composite),把图层合并成最终页面。这里有一个很重要的概念:你打开开发者工具的 Performance 面板,看到的长条就是这些阶段的耗时分布。首屏慢通常不外乎几个原因:HTML 解析被同步脚本卡住、CSS 文件太大导致 CSSOM 构建慢、图片体积太大导致下载时间过长、布局阶段重排次数太多。访问一个电商首页时,你可能会看到一个有意思的现象:先出来框架,再出图片,最后文案和详情模块慢慢填充。这是因为页面采取了“骨架屏 + 懒加载 + 分片加载”策略,通过 JS 控制内容逐步渲染,视觉上让用户觉得“页面有反应”,把后端接口慢的体感降到最低。4.3 造出一个新页面要重新走完整链路吗如果用 SPA(单页应用)架构,比如 Vue 或 React 技术栈,那么整个页面切换通常是纯前端路由跳转,不再请求新的 HTML 文档。这样你按下“下一页”的时候,浏览器并不重新执行 DNS 解析、TCP 三次握手这些流程,而只是向服务端要数据。所以现在的电商页面实际上被拆成了两部分:一个主文档框架 + 一堆接口数据。主文档框架可能一天都不变,走 CDN 强缓存;接口数据按用户实时变化,走带鉴权的动态请求。理解这个区分,是以后做前端性能优化的基础。5. 线上慢问题排查:从链路视角定位瓶颈5.1 “慢”到底慢在哪一段平时我们遇到“页面打开慢”的问题,可以先别急着改代码,先做链路性能分析。我个人排查的顺序一般是这样:第一步,看瀑布图。浏览器开发者工具里的 Network 面板,每一行就是一个请求,右侧的瀑布图很直观。先看主文档请求本身的耗时,如果这个请求就花了 3 秒,基本可以确定是网络链路或服务端处理慢;如果主文档很快,但是某个 JS 文件花了很大一部分时间,问题就可能是资源体积过大、CDN 缓存命中率低,或者脚本阻塞了渲染。第二步,把请求耗时拆开看。瀑布图里每一段有不同颜色:有 DNS 查找、建立连接、TLS 握手、发送请求、等待服务器响应、下载内容。DNS 查找时间长,检查本地 DNS 配置;TLS 握手时间长,检查证书链和加密套件;等待服务器响应时间长,基本是后端接口问题,需要去看数据库慢查询、上游依赖耗时。第三步,对比不同节点。我在实际工作中排查过一次访问慢,用户反馈在某些地区很慢,但本地复现不了。后来分别在几个主要接入点做了定时采集,发现其中一个省份的运营商 DNS 对 CDN 调度返回了异常的边缘节点 IP,导致用户实际访问时跨了很长的物理链路。这个情况通过代码层面完全看不出问题,只有链路视角才能定位。5.2 用适合的工具拆链路排查链路的时候,有几个工具我很常用:浏览器开发者工具:最基础,看瀑布图、请求头、响应头、时间和 Timing 信息,全是免费可用。curl -w:在终端手动模拟请求,拿到 DNS 解析时间、连接时间、首字节时间等指标。curl -o /dev/null -s -w "dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n" https://www.taobao.com/这条命令可以让你快速把耗时拆解到不同环节,看看到底是哪段拖了后腿。在线拨测工具:适合做地域性对比,能从多个城市发起请求,拿到全国的延迟情况。如果某个地区明显异常,再针对性排查运营商和 CDN 调度。5.3 几个容易误导排查方向的隐藏坑最后分享几个查链路慢时会干扰判断的点,都属于“看着正常但实际不正常”的情况:第一个坑:缓存命中也计入耗时。有时候瀑布图显示某个资源很快,但其实是命中了浏览器本地缓存,没有走真实网络。排查线上问题时,最好打开开发者工具的“Disable cache”选项,再把 Network 面板的缓存状态从memory cache或disk cache看成真实请求。第二个坑:图片资源的懒加载会掩盖性能问题。很多电商页面的图片是滚动到可视区域才开始加载的,如果你只盯着 Network 面板看,会发现首屏明明只有少数几个请求,页面的 image 总大小也远低于预期。这不代表页面没问题,而是因为数据还没被真正加载。需要关注的是接口返回的图片地址列表和实际触发加载的时机。第三个坑:本地环境是 HTTPS,线上是 HTTP,或者反过来。HTTPS 有握手开销,HTTP 没有,两者在请求耗时上天然就有差距,但如果页面本身同时混合了 HTTPS 和 HTTP 资源,现代浏览器会自动把 HTTP 请求升级,这个升级过程也可能带来额外的重定向开销。第四个坑:DNS 预解析和预连接。很多站点在 HTML 里写了<link rel="dns-prefetch">或<link rel="preconnect">,浏览器会提前去解析某些域名或者预先建立连接,这会使得真实访问某些跨域资源时,瀑布图上显示的时间比实际网络耗时更长。因为资源加载可能发起得非常早,甚至在主文档解析之前就开始建连。6. 关于这条链路,我最想强调的两件事既然聊到了这里,我想说两个可以马上落地的认知。一个是,链路里每一层都在努力复用和缓存。DNS 有缓存,TCP 有连接复用,TLS 有会话恢复,HTTP 有缓存头,CDN 有边缘缓存,服务端有 Redis、多级缓存,浏览器本身还有 HTTP Cache、DNS Cache、预连接等等。所谓的性能优化,本质上是把每一层的缓存和最简化路径挖掘到极致。另一个是,链路是一个整体,任何局部优化都可能被其他环节抵消。比如你把后端接口从原来的 500 毫秒降到了 100 毫秒,但如果 CDN 缓存命中率太低、或者 DNS 解析跨地域严重,用户端首屏依然快不了多少,需要整体看。我自己排查过很多类似问题,最有成就感的不是某个优化点,而是对整套链路有了全局掌控感。从那以后,不管是前端反馈的加载慢、后端反馈的连接超时、运营反馈的某地区无法访问,只要给我请求的 URL 和一个抓到的 Timing 时间,基本都能往对应的链路环节去猜,然后快速验证,这比漫无目的地翻日志高效太多了。