🌐 第四章 Web网站的运行原理
📄 一句话概括

从敲下回车到看见页面,这一路上实际执行了三个处理:DNS 的域名解析、HTTP 请求、HTTP 响应。 它们不是并列的三件事,而是一条接力链——每一棒的产出正好是下一棒的投入:域名解析把"名字"换成"IP 地址"(解决"发给谁"),HTTP 请求把"我要什么"写成一张纸递过去(解决"要什么"),HTTP 响应把东西连同说明一起送回来(解决"给什么")。链上还有三处会"拐弯":缓存命中让链条提前结束、301/302 重定向让整条链再走一遍、CDN / 反向代理让解析出来的 IP 根本不是源服务器。而这条链最重要的一条性质是:它不是一条直线,而是"主线 + N 次循环"——DNS 解析、建连接、谈加密通常只做一次,请求—响应的来回却要做很多次(页面里每个 CSS、JS、图片、字体都算一个)。搞清这条链,就同时得到两样东西:"网页是怎么打开的"的完整答案,以及"哪一步断了该查哪儿"的排查地图。

📝 本节和前面几节的关系

这一节是第四章的"收口节"——把前面十一节拆开的零件,按运行时序拼成一条完整的链。

  • 4.4 的 URL、4.5 的请求、4.6 的响应:就是本节第二、三棒的详细展开
  • 4.7 的 Cookie、4.8/4.9 的代理、4.10 的 Web 应用:全都是这条链上的"附加剧情"
  • 3.21/3.22 的 DNS、3.18 的 TCP、3.17 的端口、3.7 的 IP:本节第一棒和"建连接"那一段讲的正是它们
  • 4.11 问的是"要访问它,你得备好什么"(静态的准备清单);本节问的是"访问的时候,实际执行了什么"(动态的运行过程)。两节合起来,才是"打开一个网页"的完整回答

一、这一节到底在问什么

第四章开篇那句问话是"我们真的知道每天浏览的 Web 网站是如何工作的吗"。从 4.1 一路走到 4.11,零件基本齐了。这一节把它们装起来,回答最后一个问题:

从敲下回车,到页面上出现内容,这段时间里到底执行了哪些处理?

教材把答案收成三段:

处理 它解决什么 一句话说清
DNS 的域名解析 发给谁 把"名字"翻译成"IP 地址"
HTTP 请求 要什么 把"要哪一份东西"写成一张纸递过去
HTTP 响应 给什么 把东西连同"它是什么"的说明一起送回来
💡 为什么是这三段

因为一次访问必须依次解决三个问题,而且解决顺序不能颠倒:不知道对方在哪台机器(第一段没完成),就无从发请求(第二段开始不了);没有请求递过去,也就没有响应回来(第三段无从谈起)。

二、三个处理,三段接力

三段的关系不是"三件并列的事",而是一条交接清楚的接力链:

      第一棒                 第二棒                  第三棒
   DNS 域名解析    ──→     HTTP 请求     ──→     HTTP 响应
   拿到:一串域名          拿到:IP + 路径          拿到:请求报文
   给出:一个 IP 地址      给出:一张请求报文       给出:状态码 + 头部 + 正文
   (3.21 · 3.22)        (4.4 · 4.5)           (4.5 · 4.6)

每一棒的"给出",正是下一棒的"拿到"。 这句话听起来像废话,但它有一个很实际的推论:

任何一棒断了,后面全都不会发生。 —— 解析不出来,就建不了连接;连不上,就发不出请求;请求没送出去,就不会有响应。

而且三棒里,只有第一棒(DNS 解析)通常只做一次,第二、三棒要来回很多遍——这一点后面第六节会讲清。

三、第一棒:DNS 的域名解析

1. 为什么不能跳过这一步

收 3.21 讲过,DNS 是"网络的电话簿":人记域名,机器认 IP 地址。

麻烦就在这里——你在地址栏里敲的是 shop.example.com,但数据真正要发到某个 IP 地址上。中间这层"翻译"就是第一棒要干的活。而干这活的,是浏览器里那个 DNS 解析模块(收 3.22)。

2. 它不是"问一次就完了"——有缓存层次

这一层是本节值得补上的:同一件事要按顺序往下找,找到就停。

顺序 查哪儿 命中就意味着
① 浏览器自己的 DNS 缓存 刚才访问过这个站,直接用
② 操作系统的 DNS 缓存 / hosts 文件 本机记着,直接用
③ 配置好的 DNS 服务器(路由器、运营商、8.8.8.8、114.114.114.114) 它替你去递归查询,结果按 TTL 缓存
④ 一级级往上问:根 → 顶级域 → 权威服务器 拿到最终答案

第③层的缓存能存多久,由那条记录的 TTL 决定(收 3.22 的资源记录)。TTL 没到,再来问就直接给你上一次的答案——这就是"第一棒通常不用真的往外跑"的原因。

3. 解析结果不是固定的

一个值得记住的事实:同一个域名,不同时间、不同地点、不同网络,解析出来的 IP 可能不一样。

原因不是出错,而是有意设计:CDN 与就近接入,正是靠"让不同地区的用户解析到离自己最近的那台缓存节点"来加速的(收 4.8 讲的反向代理)。所以你解析出来的那个 IP,很可能根本不是源服务器。

4. 这一棒的产出

一个 IP 地址。 拿到它,第一棒的任务就完成了,交接给第二棒。

四、第二棒:HTTP 请求

1. 严格说,发请求之前还有两小步

拿到 IP 之后不能直接开口说话,得先把"路"接上:

这两小步合起来,就是"准备工作"里那六步的第④⑤步。它们是第二棒的前置动作,也是"贵的部分"——后面会看到,这两步同样通常只做一次。

2. 请求报文本身:三段结构

收 4.5 讲过的规矩,这里只强调两处最容易忘的:

收 4.4 那句结论在这里兑现:服务器永远只看到"路径 + 查询串"。 你在地址栏里敲的六段,到这一棒只剩两段装进了那张纸。

3. 浏览器替你塞进去的一堆东西

这一条很值钱:你什么都没写,但那张纸上已经贴满了标签。

头 谁塞的 干什么用
Host 浏览器 告诉服务器要访问哪个网站(同一台机器上可能跑着很多个站)
User-Agent 浏览器 说明"我是谁",服务器据此返回不同版本
Accept / Accept-Encoding 浏览器 说明"我能收哪些格式、支不支持压缩"
Cookie 浏览器自动翻出来带上 收 4.7:这张纸条是它自己记着的
If-None-Match 浏览器 收 4.6:协商缓存,问服务器"我手上这份还新吗"
Referer 浏览器 说明"我是从哪个页面跳过来的"
📝 为什么 F12 里看到的请求总比自己想的长

因为你只写了地址,剩下的全是浏览器按规矩替你补的。这也解释了为什么"手动拼一个请求"很难完全模拟浏览器——缺了某些头,服务器给的东西会不一样。

4. 一个页面不止一个请求

收 4.1 讲过的:HTML 里每个 CSS、JS、图片都只是引用,各要单独要一回。

所以第二棒和第三棒会反复来回——这才是"几十上百次请求"的真实来源(后面第六节展开)。

5. 这一棒的产出

一张请求报文,交到服务器手上。

五、第三棒:HTTP 响应

服务器那边接过请求之后做的事,收 4.6 讲得很细,这里按"这一棒"的视角收一遍:

服务器执行四步:收下请求 → 找到内容(静态读文件 / 动态跑程序)→ 装好包装 → 发出响应。

响应报文也是三段(状态行 + 头部 + 空行 + 正文),其中三样最关键:

三样东西 回答什么问题
状态码 结果如何?(4 开头怪自己,5 开头怪服务器——收 4.5)
Content-Type 这是什么?「类型/子类型」直接决定浏览器怎么处理
Content-Length / Transfer-Encoding 有多长、怎么读(定长还是分块)

一个容易忽略的点:响应里装的不一定是网页

同一套 HTTP,运的东西五花八门:

正文是什么 谁消费它
HTML 渲染引擎(画成页面)
CSS / JS 渲染引擎与脚本引擎
图片 / 字体 渲染引擎(当资源用)
JSON 脚本(不是给人看的) —— 收 4.10
纯数据 / 文件 下载下来,或交给别的程序

判断依据永远是那个 Content-Type 头,不是文件后缀,也不是"看起来像什么"。

这一棒的产出

一段字节流 + 一份说明,交给浏览器渲染引擎。到这里,三个处理都完成了——但一次访问往往到这里才刚过半。

六、串成一条线:一次访问的完整时序

把三段接起来,从敲回车到看见页面:

# 发生了什么 收哪一节
1 你在地址栏敲下地址,回车 —
2 拆 URL:协议 / 主机 / 端口 / 路径 4.4
3 查缓存:本地存过这份吗?还新鲜吗?新鲜就直接用,整条链到此结束 4.6
4 DNS 解析:域名 → IP 地址(第一棒) 3.22
5 建 TCP 连接:和那个 IP 的端口三次握手 3.18
6 谈 TLS(https 才有):验证证书、商定密钥 4.11
7 发 HTTP 请求(第二棒) 4.5
8 服务器处理:找内容 → 装包装 → 发出 4.6
9 回 HTTP 响应(第三棒) 4.5
10 浏览器渲染:解析成树 → 应用样式 → 画出来 4.2 / 4.3
11 发现页面里还有别的资源(CSS、JS、图片……)→ 回到第 7 步再来一遍,循环 N 次 4.1
12 全部到齐,页面才算完整 —
❗ 这条链最值得记住的一条性质

它不是一条直线,而是"主线 + N 次循环"。

第 4、5、6 步(DNS 解析、建连接、谈加密)——通常只做一次,因为解析出的 IP 和建好的连接会被复用。第 7~9 步(请求 → 处理 → 响应)——要做很多次,页面里每个资源各一回。

这就是"几十个请求却还不慢"的真正原因:贵的部分只做一次,便宜的部分做很多次。如果你觉得页面慢,往往就是因为这几步里某一步被重复做了(比如每次请求都重新建连接)。

七、三个会"拐弯"的地方

上面那条链画得太顺了。实际跑起来,有三处会让它拐个弯——这也是本节最容易漏掉的细节。

1. 缓存命中:链条提前结束

第 3 步查缓存强缓存命中时,整条链连第一棒都不做,直接从本地拿出来显示(收 4.6)。

这就是"第二次打开同一个页面特别快"的原因——不是网变快了,是活儿根本没那么干了。

2. 301 / 302 重定向:整条链再走一遍

收到的响应不是内容,而是一句"东西搬家了,去 Location 里那个地址拿"(收 4.5 的状态码家族)。

浏览器会拿着新地址重新走一遍——建连接、发请求、收响应,如果换了域名还要重新做一次 DNS 解析。

所以 F12 里会看到多出来一轮请求。这不是重复,是规矩要求它再走一遍。

3. CDN / 反向代理:解析出来的 IP 根本不是源服务器

第 4 步解析得到的那个 IP,可能只是离你最近的缓存节点(收 4.8)。你拿到的内容可能是它替你取回来的、甚至是它早就存好的。

所以"同一个域名,不同人解析到不同 IP"不是异常——这正是大型网站让全世界都访问得快的方式。

还有一种"拐弯":响应里还藏着子资源

第 11 步那个循环,本质上也是"拐弯":一个响应里可以再引出好几个请求。CSS 里引字体、JS 里调接口、页面里嵌图片——层层套下去,所以页面是"一点点长出来"的(收 4.10 讲的 Web 应用,还会让这个过程变得更频繁)。

八、三段失败,三种现象

这条链有一个非常实际的用法:哪一棒断了,现象是不一样的。

断在哪一段 你看到的现象 该先查什么
DNS 解析 "找不到服务器"/ DNS 相关报错 域名拼错没、DNS 服务器设置、hosts 文件
建连接 "无法访问此网站"、连接超时 网络通不通、端口、防火墙、代理设置残留
请求 → 响应 有回应但内容不对:404 / 403 / 500 看状态码第一位:4 怪自己,5 怪服务器
渲染 / 子资源 页面出来了,但样式乱、图缺 F12 里看具体哪个资源失败了
💡 和 4.11 那张表的区别

4.11 那张表是按"客户端模块"分的(缓存、Cookie、代理、扩展);这张表是按"三个处理"分的(解析、连接、请求响应)。同一件事的两个切法——排障时哪个顺手用哪个。

一条最实用的经验:"页面本身打不开"和"页面开了但缺东西",是完全不同的问题。 前者断在前两棒,后者只是某个子资源自己没拿到——页面是好的。

九、动手:Demo49 把三段分开看

🧪 目标

不靠"感觉",而是把三个处理一段一段地单独拎出来看,并且亲手验证"第一棒通常只做一次"。

第 1 步|只看第一棒。 命令行敲:

nslookup example.com

得到一个 IP 地址——这就是第一棒的产出。

第 2 步|换个 DNS 服务器再问一次。

nslookup example.com 8.8.8.8

对比两次结果。不一样是正常的——第一棒的结果可以被有意设计。

第 3 步|看 DNS 缓存。

ipconfig /displaydns

你会看到一长串"刚查过的域名 + TTL 倒计时"。这就是"第一棒通常只做一次"的证据——本机记着答案,短期内不用再问。

第 4 步|只看第二、三棒。

curl -v http://example.com/

> 开头的那几行是请求报文(第二棒),< 开头的那几行是响应报文(第三棒)。一条命令,两棒分得清清楚楚。

第 5 步|只取响应头部。

curl -I https://example.com/

这就是第三棒的前半段——状态码和头部,正文不要。

第 6 步|让"第一棒失败"长什么样。 把域名换成一个不存在的:

curl -v http://no-such-host-abcxyz.com/

报的是解析失败,而不是连接失败——因为第一棒就没过去,第二棒压根没开始。这一步是本节的核心现场。

第 7 步|F12 计时栏对时序。 刷新一次页面,看"计时"那一栏的分段:

排队 → DNS 查找 → 连接 → TLS → 等待 → 下载

这六段正好对应上面时序的第 3~9 步。哪段长,问题就在哪段。

第 8 步|30 秒实验。 数一下这一次访问一共发了多少个请求。回头再看 4.1 那句"几十上百次请求"——现在你能自己解释了:因为第 7~9 步被循环了 N 次。

十、常见误区

  1. "DNS 解析每次访问都要做一遍。" —— 有多层缓存(浏览器 → 系统 → DNS 服务器),同一个域名的解析结果通常被缓存复用。一次访问里往往只做一次,TTL 决定多久后要重新问。
  2. "一个网页只发一个 HTTP 请求。" —— 主页 + 每个 CSS、JS、图片、字体各一次。第二、三棒要反复来回。
  3. "域名解析出来的 IP 是固定的。" —— 不同时间、地点、网络可以解析到不同 IP。CDN 与就近接入正是靠这个工作的。
  4. "请求和响应是同一件事的两面,可以合并理解。" —— 它们是两方各自执行的处理:请求是浏览器写的,响应是服务器写的。方向、内容、负责人都不同。
  5. "访问慢就是网速慢。" —— 慢可能出在任何一段:DNS 慢、握手慢、服务器慢(等待耗时长)、文件大(下载慢)。必须按段看。
  6. "URL 里写了域名,所以服务器知道我在访问哪个域名。" —— 服务器是从 Host 头知道的;请求行里只有路径和查询串(收 4.4)。
  7. "响应里一定是网页。" —— 可能是 HTML、CSS、JS、JSON、图片、字体……Content-Type 说了算(收 4.6)。
  8. "收到 200 就说明页面对了。" —— 200 只说明这一个请求被正确处理。页面里某张图 404,主页照样是 200。
  9. "DNS 解析成功就说明网络是通的。" —— 解析成功只说明问到了 DNS 服务器。后面的建连接、谈加密、发请求都可能失败。
  10. "重定向只是换个地址而已。" —— 301/302 会让浏览器从建连接那一步重新来过(换域名时还要重做 DNS 解析)。一次访问变成两轮。

十一、动手自测

❓ 自测一下
  1. 本节标题点名的三个处理分别是哪三个?各自解决什么问题?
  2. 为什么说这三棒是"接力"关系?
  3. DNS 解析要依次查哪几层?哪一层决定"多久之后要重新问"?
  4. 为什么同一个域名在不同时间能解析出不同的 IP?
  5. 发出 HTTP 请求之前,还要先完成哪两件事?
  6. 请求报文里为什么没有域名?服务器怎么知道你要访问哪个站?
  7. 一次访问里,DNS 解析通常做几次?请求—响应来回几次?为什么?
  8. 响应里有哪些东西决定"浏览器拿到之后怎么处理"?
  9. 响应是 301/302 时会发生什么?
  10. "页面打不开"和"页面开了但某张图没出来",分别断在哪一段?

答案: ① DNS 的域名解析(从域名找到 IP——解决"发给谁")、HTTP 请求(告诉服务器要什么——解决"要什么")、HTTP 响应(服务器把东西送回来——解决"给什么")。② 因为每一棒的产出正好是下一棒的投入:解析出 IP 才能建连接,连上了才能发请求,请求递进去才有响应出来。任何一棒断了,后面全都不会发生。 ③ 依次查:浏览器自己的 DNS 缓存 → 操作系统的缓存 / hosts 文件 → 配置的 DNS 服务器(它替你递归查询)→ 根 / 顶级域 / 权威服务器。DNS 服务器上那条记录的 TTL 决定它缓存多久、多久后重新问。④ 因为结果可以被有意设计:CDN 与就近接入让不同地区的用户解析到离自己最近的节点,负载均衡也会让同一域名解析到不同服务器。⑤ 先建 TCP 连接(三次握手,收 3.18);如果网址是 https,还要先谈好 TLS(收 4.11)。⑥ 因为同一台服务器上可以跑很多个网站,光靠 IP 分不出来。域名写在 Host 头里,服务器靠它判断你要访问哪个站(收 4.4)。⑦ DNS 解析通常只做一次——解析出的 IP 会被缓存复用;请求—响应的来回通常做很多次——页面里每个 CSS、JS、图片、字体都要各要一回。这就是"几十上百次请求却还不慢"的原因:贵的部分(解析、连接、加密)只做一次,便宜的部分(一次请求)做很多次。 ⑧ 状态码(结果如何)、Content-Type(「类型/子类型」决定浏览器怎么处理)、Content-Length 或 Transfer-Encoding(有多长、怎么读)。另外 Cache-Control / ETag 决定下次还要不要重新下(收 4.6)。⑨ 浏览器不显示这份内容,而是拿着 Location 头里的新地址重新走一遍整条链(换域名时还要重新做 DNS 解析)。所以 F12 里会看到多出来一轮请求。⑩ "打不开":断在前两棒——先看 DNS 解析(域名能不能解析出 IP),再看建连接(能不能连上)。"开了但某张图没出来":页面本身是好的,断在那个图片资源自己的请求上(F12 里能看到它是 404 还是超时)。

十二、下一步学什么

相关笔记