Back to Notes

计算机网络 - 网络架构

This post is not yet available in English. Showing the original version.
Table of Contents
Table of Contents
层级层名称核心机构与企业代表性贡献
5应用层 (Application)IETF, W3C, CERN, NCSA, QualcommIETF/W3C: 制订 HTTP, HTML, DNS 标准
CERN: 发明万维网 (World Wide Web)
NCSA: 推出 Mosaic 浏览器
Qualcomm: POP3 电子邮件协议
4传输层 (Transport)IETF, UC Berkeley, Google, UMass AmherstIETF: 推动 TCP, UDP, QUIC 标准化
UC Berkeley: 在 BSD Unix 中实现 TCP/IP
Google: 推动 QUIC 协议发展
UMass Amherst: 在网络建模、性能分析和高速协议方面的先驱性研究
3网络层 (Network)IETF, DARPA, Stanford, Cisco, UT AustinDARPA/Stanford: 资助与开发 TCP/IP
Cisco: 开发路由协议 (EIGRP, OSPF)
UT Austin: 1969年 ARPANET 最初的4个节点之一;参与了历史上首次网络消息传递。
2链路层 (Data Link)IEEE, Xerox PARC, Univ. of CambridgeIEEE: 制订 Ethernet (802.3) 和 Wi-Fi (802.11) 标准
Xerox PARC: 发明 Ethernet (Robert Metcalfe)
Univ. of Cambridge: 发明 Cambridge Ring
1物理层 (Physical)IEEE, ITU, Bell Labs, AT&TIEEE/ITU: 制订线缆、光纤、无线电等通信标准
Bell Labs/AT&T: 发明晶体管、推动数字传输与 T-carrier 系统

一、网络应用架构(Application Architecture)

在开始讨论具体的应用层协议之前,需要先理解网络应用的架构设计。这不是指网络本身的协议栈分层,而是开发者在设计网络应用时所选择的通信组织方式

主要有两种范式:

架构核心思想代表应用
客户端-服务器(Client-Server)有一个永远在线的服务器,客户端向它发起请求Web、电子邮件、网银
对等网络(Peer-to-Peer, P2P)没有固定服务器,终端之间直接通信BitTorrent、区块链、早期 Skype

注意区分:这里说的是应用层架构,不是网络层的拓扑结构。底层的 Internet 始终是分组交换网络,但在应用层,你可以选择让程序以 C/S 模式还是 P2P 模式运行。


二、客户端-服务器范式(Client-Server Paradigm)

2.1 基本模型

graph TD
    Server["服务器\n永远在线 · 固定 IP\n(通常在数据中心)"]
    C1["客户端 1\n间歇性连接 · 动态 IP"]
    C2["客户端 2\n间歇性连接 · 动态 IP"]
    C3["客户端 N\n间歇性连接 · 动态 IP"]
    C1 -->|"请求/响应"| Server
    C2 -->|"请求/响应"| Server
    C3 -->|"请求/响应"| Server
    C1 -.-x|"不直接通信"| C2

2.2 服务器端的特征

特征说明
永远在线(Always-on)服务器必须 7×24 小时运行,随时等待客户端请求
固定 IP 地址服务器需要一个永久的、已知的 IP 地址(或域名),以便客户端能找到它
部署在数据中心为了应对大量并发请求,服务器通常部署在拥有高带宽和冗余电力的数据中心
可扩展性通过增加服务器数量(水平扩展)来应对增长的请求量

2.3 客户端的特征

特征说明
间歇性连接客户端不需要永远在线,需要时才连接
动态 IP 地址大多数客户端的 IP 由 ISP 动态分配(DHCP),每次联网可能不同
不直接通信在纯 C/S 模型中,客户端之间不直接通信,所有交互都经过服务器
发起请求通信总是由客户端发起(服务器被动等待)

为什么客户端之间不直接通信? 因为客户端的 IP 地址是动态的、不可预测的,而且它们可能在 NAT(网络地址转换)后面,从外部根本无法直接访问。只有服务器拥有固定 IP,所以一切通信都要经过它。

2.4 C/S 模型的可扩展性问题

随着用户数量增长,单台服务器的处理能力终将不够。解决方案:

graph TD
    Client1["客户端 1"] --> LB["负载均衡器"]
    Client2["客户端 2"] --> LB
    Client3["客户端 N"] --> LB
    LB --> S1["服务器 1"]
    LB --> S2["服务器 2"]
    LB --> S3["服务器 N"]
    S1 --> DB["共享数据库 / 缓存"]
    S2 --> DB
    S3 --> DB
扩展策略说明
垂直扩展(Scale Up)给单台服务器加 CPU、内存、带宽——简单但有上限
水平扩展(Scale Out)增加服务器数量,用负载均衡器分配请求——理论上可无限扩展
数据中心集群Google、Amazon 等在全球部署多个数据中心,结合 CDN 就近服务用户

现实数据:Google 在全球拥有 30+ 个数据中心,仅搜索服务每天就要处理约 85 亿次请求。这种量级只能通过大规模水平扩展实现。


三、P2P 范式(Peer-to-Peer)

3.1 基本模型

graph LR
    A["Peer A"] <-->|"数据交换"| B["Peer B"]
    A <-->|"数据交换"| C["Peer C"]
    A <-->|"数据交换"| D["Peer D"]
    B <-->|"数据交换"| C
    B <-->|"数据交换"| D
    C <-->|"数据交换"| D

没有永远在线的服务器!每个 Peer 既是客户端,也是服务器。

3.2 P2P 的核心特征

特征说明
无专用服务器不存在一个永远在线的中心服务器(或者中心服务器只做极少量的协调工作)
Peer 既是客户又是服务者每个节点同时请求服务和提供服务——自扩展(self-scalability)
间歇性连接Peer 随时可能上线或下线,IP 地址也是动态的
管理复杂没有中心化控制,安全性、内容一致性等问题更难处理

自扩展性(Self-scalability):这是 P2P 最强大的特性。在 C/S 模型中,用户增加 = 服务器负载增加;但在 P2P 中,每个新用户既带来需求也带来供给(既下载也上传),所以系统的总服务能力随用户增加而自动增长。

3.3 C/S vs P2P 文件分发效率对比

这是一个经典的分析问题。假设一个服务器要把大小为 FF 的文件分发给 NN 个客户端。

C/S 模型下的分发时间下界:

DCSmax{NFus,Fdmin}D_{CS} \geq \max\left\{\frac{NF}{u_s}, \frac{F}{d_{\min}}\right\}

P2P 模型下的分发时间下界:

DP2Pmax{Fus,Fdmin,NFus+i=1Nui}D_{P2P} \geq \max\left\{\frac{F}{u_s}, \frac{F}{d_{\min}}, \frac{NF}{u_s + \sum_{i=1}^{N} u_i}\right\}
xychart-beta
    title "文件分发时间 vs 用户数"
    x-axis "用户数 N" [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
    y-axis "分发时间"
    line "C/S(线性增长)" [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    line "P2P(亚线性增长)" [1, 1.3, 1.5, 1.6, 1.7, 1.75, 1.8, 1.83, 1.86, 1.9]

直觉理解:想象一个老师(服务器)要把一份讲义发给全班 50 人。C/S 模式就是老师一个一个复印发放,50 个人要印 50 次。P2P 模式就是老师印 1 份给第一个同学,然后让拿到的同学也帮忙复印——很快每个人都在帮忙复印,分发速度飞快。

3.4 BitTorrent 协议

BitTorrent 是最具代表性的 P2P 文件分发协议。

基本概念

术语含义
Torrent参与分发同一文件的所有 Peer 的集合
Tracker跟踪所有参与 Peer 的服务器(告诉新加入的 Peer "谁有这个文件")
Chunk文件被分成等大的块(通常 256 KB),以 Chunk 为单位交换
Seed(做种者)已拥有完整文件、只上传不下载的 Peer
Leecher(吸血者)还没下完文件、正在下载的 Peer

核心机制

1. 最稀有优先(Rarest First)

下载时优先请求在整个 Torrent 中副本数最少的 Chunk。

为什么? 如果某个 Chunk 只有 1 个 Peer 拥有,而那个 Peer 下线了,这个 Chunk 就永远无法获取了。优先请求稀有 Chunk 可以让所有 Chunk 尽快在网络中均匀分布。

2. Tit-for-Tat(以牙还牙)

每个 Peer 优先向给自己上传速度最快的 4 个 Peer 上传数据(称为 unchoked peers),每 10 秒重新评估一次。同时每 30 秒随机选一个 Peer 上传(optimistic unchoking)。

博弈论解读:这是一种激励兼容(incentive-compatible) 设计——你上传得越多,别人就越愿意给你上传。"白嫖"的 Peer 会被"惩罚"(choked),因为没人愿意给不上传的人传数据。这也是为什么有些 BT 客户端强调"做种比率"。


四、混合架构

实际上很多应用采用混合架构——核心功能用 C/S,但某些部分用 P2P。

应用C/S 部分P2P 部分
即时通讯(早期 QQ/微信)用户注册、登录、好友列表存储在中心服务器用户之间的消息传输可以是直连的(端到端)
Skype(早期)用户认证走中心服务器语音/视频数据在用户间直传,超级节点帮助 NAT 穿透
BitTorrentTracker 是 C/S 模式(告诉你谁有文件)文件传输本身是 P2P

现代趋势:随着云计算成本下降,很多原本使用 P2P 的应用已经转向 C/S(如 Skype 现在完全由 Microsoft 服务器中继)。但 P2P 在去中心化场景(区块链、IPFS)中依然不可替代。


五、进程通信(Process Communication)

网络应用的本质是不同主机上的进程之间的通信。理解"进程如何通过网络交流"是理解所有应用层协议的基础。

5.1 进程与套接字(Socket)

进程(Process):运行在主机上的程序实例。

在同一台主机上,进程间通信由操作系统管理(如管道、共享内存)。但在不同主机上,进程必须通过网络交换消息。

graph TD
    subgraph 主机A["主机 A"]
        AppA["应用进程(浏览器)"]
        SocketA["Socket"]
        TransA["传输层(TCP/UDP)"]
        NetA["网络层 ..."]
        AppA --> SocketA --> TransA --> NetA
    end
    subgraph 主机B["主机 B"]
        AppB["应用进程(Web 服务器)"]
        SocketB["Socket"]
        TransB["传输层(TCP/UDP)"]
        NetB["网络层 ..."]
        AppB --> SocketB --> TransB --> NetB
    end
    NetA <-->|"互联网"| NetB

套接字(Socket) 是进程与网络之间的接口(门)

类比:进程是房间里的人,Socket 是房间的门。你把信(数据)交给门口(Socket),邮递员(传输层)帮你送到对面房间的门口。你不需要知道邮递员怎么走的,只管把信塞进门就行。

概念解释
Socket应用层与传输层之间的 API 接口
发送方进程把消息推入 Socket
接收方进程从 Socket 拉出消息
开发者控制Socket 以上的应用层逻辑
OS 控制Socket 以下的传输层、网络层等

5.2 进程寻址(Addressing)

要把消息送到正确的进程,需要两个信息:

标识作用例子
IP 地址标识哪台主机142.250.80.46(Google 的一个 IP)
端口号(Port Number)标识主机上的哪个进程80(HTTP)、443(HTTPS)、25(SMTP)

类比:IP 地址就像一栋大楼的门牌号,端口号就像楼里的房间号。邮件上不仅要写"XX路123号"(IP),还要写"5楼501室"(端口),快递员才知道送给谁。

知名端口号(Well-known Ports):0-1023 号端口被系统保留给标准服务。

端口号协议服务
20, 21FTP文件传输
22SSH安全远程登录
25SMTP发送电子邮件
53DNS域名解析
80HTTP网页浏览
443HTTPS加密网页浏览

六、应用层协议定义的内容

一个应用层协议需要定义以下几个方面:

维度内容例子(HTTP)
消息类型有哪些种类的消息请求(Request)、响应(Response)
消息语法(Syntax)消息中有哪些字段、如何排列请求行、头部字段、空行、消息体
消息语义(Semantics)各字段的含义Content-Type: text/html 表示正文是 HTML
规则(Rules)何时、如何发送和响应消息客户端先发请求,服务器收到后回响应

协议的开放性:

类型说明例子
开放协议由 RFC(Request for Comments)标准文档定义,任何人都能实现HTTP、SMTP、DNS
私有协议由公司自行定义,不公开细节Skype 早期协议、某些游戏的通信协议

RFC 是由 IETF(Internet Engineering Task Force)发布的互联网标准文档。例如 HTTP/1.1 定义在 RFC 2616(后被 RFC 7230-7235 取代),任何开发者都可以据此实现自己的 Web 服务器或浏览器。


七、应用对传输服务的需求

不同的网络应用对传输层有不同的需求,可以从四个维度来分析:

7.1 四个维度

维度含义高要求的应用低要求/容忍的应用
数据完整性(Data Integrity)数据是否能容忍丢失文件传输、网银、电子邮件(零容忍音视频通话(丢几个包人耳察觉不到)
吞吐量(Throughput)需要多少最低带宽保障视频流媒体(需要稳定高吞吐)电子邮件(多慢都能忍)
时延(Timing / Delay)对延迟的敏感程度在线游戏、IP 电话(< 150ms)文件下载(延迟无所谓)
安全性(Security)需要加密、认证等网银、电子商务公开网页浏览(低要求)

7.2 带宽敏感 vs 弹性应用

类型含义代表应用
带宽敏感型(bandwidth-sensitive / inelastic)需要最低带宽保障才能正常工作视频流(720p 至少需要 ~2.5 Mbps)、VoIP
弹性应用(elastic)带宽多少都能用,越多越好文件传输、Web 浏览、电子邮件

7.3 常见应用的需求总结

应用数据完整性吞吐量时延典型传输协议
文件传输不能丢弹性不敏感TCP
电子邮件不能丢弹性不敏感TCP
Web 浏览不能丢弹性较敏感TCP
实时音视频可容忍少量丢失带宽敏感极敏感(< 150ms)UDP / RTP
在线游戏可容忍少量丢失较低极敏感UDP
即时消息不能丢弹性较敏感TCP

八、传输层协议选择:TCP vs UDP

应用开发者在创建 Socket 时需要选择使用 TCP 还是 UDP。

8.1 TCP 提供的服务

服务说明
面向连接(Connection-oriented)通信前需要三次握手(3-way handshake)建立连接
可靠传输(Reliable Transport)保证数据完整、有序到达,丢了会重传
流量控制(Flow Control)发送方不会淹没接收方(根据接收方能力调节速率)
拥塞控制(Congestion Control)网络拥塞时自动降速,避免加剧拥塞
不提供:最低带宽保障、延迟保障、安全性这些需要应用层自行实现(如 TLS 加密)

8.2 UDP 提供的服务

服务说明
无连接(Connectionless)不建立连接,直接发送
不可靠传输不保证送达、不保证顺序、不重传
无流量/拥塞控制想发多快就发多快
轻量高效头部开销小(仅 8 字节 vs TCP 的 20+ 字节),延迟低

那 UDP 有什么用? 看起来 UDP 什么都不保证,为什么还有人用?因为 "不保证"换来了"快"和"自由"。对于实时性要求极高的应用(游戏、视频通话),与其等 TCP 重传一个已经过时的数据包,不如直接丢掉继续——用户感觉不到丢了一帧画面,但一定能感觉到 200ms 的延迟卡顿。

8.3 TCP 与 TLS:安全层的加持

TCP 本身不提供加密。TLS(Transport Layer Security) 在 TCP 之上提供:

graph TD
    App["应用层数据"] --> TLS["TLS 加密\n(应用层实现,SSL/TLS 库)"]
    TLS --> TCP["TCP 可靠传输"]
    TCP --> IP["IP 路由"]

TLS 严格来说不是传输层协议,而是在应用层和传输层之间的一个"安全层"。HTTPS = HTTP + TLS。


九、Socket 编程基础

9.1 TCP Socket 编程流程

sequenceDiagram
    participant S as 服务器
    participant C as 客户端
    Note over S: 1. 创建 Socket
    Note over S: 2. bind(端口)
    Note over S: 3. listen()
    Note over C: 1. 创建 Socket
    Note over S: 4. accept()(阻塞等待)
    C->>S: TCP 三次握手
    S-->>C: 连接建立
    Note over C: 2. connect() 返回
    C->>S: send() 数据
    S->>C: send() 数据
    Note over S,C: 5/3. recv/send 数据来回传输
    Note over S: 6. close()
    Note over C: 4. close()

关键区分:服务器有两个 Socket——一个用于 listen 等待新连接(欢迎 Socket / welcome socket),accept() 返回的是另一个专门用于和这个客户端通信的 连接 Socket(connection socket)。这样服务器可以同时和多个客户端通信。

9.2 UDP Socket 编程流程

sequenceDiagram
    participant S as 服务器
    participant C as 客户端
    Note over S: 1. 创建 Socket
    Note over S: 2. bind(端口)
    Note over C: 1. 创建 Socket
    Note over S: 3. recvfrom()(阻塞等待)
    Note over S,C: 无需建立连接!
    C->>S: 2. sendto()(附带目的 IP + 端口)
    S->>C: 4. sendto()(附带目的 IP + 端口)
    Note over C: 3. recvfrom()
    Note over S: 5. close()
    Note over C: 4. close()
对比TCP SocketUDP Socket
建立连接需要(connect / accept)不需要
发送数据send() — 不需指定目的地(连接已建立)sendto() — 每次都要指定目的 IP + 端口
接收数据recv()recvfrom() — 同时获知发送者的地址
服务器 Socket 数2 个(welcome + connection)1 个(所有客户端共用)

UDP 没有连接的后果:UDP 服务器用同一个 Socket 服务所有客户端。它每次 recvfrom() 收到一个数据报时,必须检查"这是从哪个客户端来的"才知道要回复给谁。而 TCP 每个客户端有专属的 connection socket,所以不存在这个问题。


十、Web 与 HTTP 协议

10.1 基本概念

概念说明
Web 页面(Web Page)由多个对象(object) 组成:HTML 文件、图片、JS、CSS 等
URL统一资源定位符,格式:协议://主机名:端口/路径
HTTPHyperText Transfer Protocol,Web 的应用层协议

HTTP 使用 TCP 作为传输协议。客户端发起 TCP 连接(默认端口 80),然后在 TCP 连接上交换 HTTP 报文。

HTTP 是无状态的(Stateless):服务器不保留任何关于客户端之前请求的信息。你刚访问了首页,再访问另一页时服务器并不"记得"你。(后来用 Cookie 弥补了这一点。)

10.2 持久连接 vs 非持久连接

非持久 HTTP(HTTP/1.0):

每个对象需要一个单独的 TCP 连接。一个包含 10 张图片的网页需要建立 11 个 TCP 连接(1 个 HTML + 10 个图片)。

sequenceDiagram
    participant C as 客户端
    participant S as 服务器
    rect rgb(230, 240, 255)
        Note over C,S: 对象 1
        C->>S: TCP 连接建立(3次握手)
        C->>S: HTTP 请求
        S->>C: HTTP 响应
        C->>S: TCP 连接关闭
    end
    rect rgb(255, 240, 230)
        Note over C,S: 对象 2(又要重来一遍!)
        C->>S: TCP 连接建立
        C->>S: HTTP 请求
        S->>C: HTTP 响应
        C->>S: TCP 连接关闭
    end

非持久 HTTP 的响应时间:

每个对象的时间=2×RTT+文件传输时间\text{每个对象的时间} = 2 \times RTT + \text{文件传输时间}

持久 HTTP(HTTP/1.1 默认):

一个 TCP 连接上可以传输多个对象,服务器在发送响应后不关闭连接。

sequenceDiagram
    participant C as 客户端
    participant S as 服务器
    C->>S: TCP 连接建立
    rect rgb(230, 255, 230)
        Note over C,S: 同一个 TCP 连接!
        C->>S: HTTP 请求 1
        S->>C: HTTP 响应 1
        C->>S: HTTP 请求 2
        S->>C: HTTP 响应 2
        C->>S: HTTP 请求 3
        S->>C: HTTP 响应 3
    end
    Note over C,S: ...空闲超时后关闭

流水线(Pipelining):HTTP/1.1 还支持客户端不等响应就连续发出多个请求,进一步减少等待时间。但由于实现上的"队头阻塞"问题,实际效果有限,HTTP/2 用多路复用彻底解决了这个问题。

10.3 HTTP 报文格式

请求报文:

GET /index.html HTTP/1.1\r\n          ← 请求行(方法 URL 版本)
Host: www.example.com\r\n             ← 头部字段
User-Agent: Mozilla/5.0\r\n
Connection: keep-alive\r\n
Accept-Language: zh-CN\r\n
\r\n                                   ← 空行(头部结束标志)
                                       ← 消息体(GET 一般没有)
HTTP 方法用途有消息体?
GET获取资源
POST提交数据(表单、上传)
PUT上传/替换资源
DELETE删除资源一般无
HEAD只获取头部(不要正文)

响应报文:

HTTP/1.1 200 OK\r\n                    ← 状态行(版本 状态码 短语)
Date: Mon, 30 Mar 2026 08:00:00 GMT\r\n
Content-Type: text/html\r\n
Content-Length: 6821\r\n
Connection: keep-alive\r\n
\r\n
<html>...</html>                       ← 消息体
状态码含义常见场景
200 OK请求成功正常返回页面
301 Moved Permanently资源永久移动网站换了域名
304 Not Modified资源未修改(用缓存的)浏览器缓存命中
400 Bad Request请求语法错误参数格式不对
404 Not Found资源不存在页面被删了或 URL 打错了
505 HTTP Version Not Supported服务器不支持该 HTTP 版本极少见

10.4 Cookie:为无状态的 HTTP 添加状态

HTTP 本身无状态,但很多应用需要"记住用户"。Cookie 机制通过四个组件实现:

sequenceDiagram
    participant C as 客户端
    participant S as 服务器
    rect rgb(230, 240, 255)
        Note over C,S: 首次访问
        C->>S: GET /index.html
        S->>C: HTTP 响应 + Set-Cookie: id=1678
        Note over S: 服务器创建 Cookie,存入数据库
        Note over C: 浏览器保存 Cookie 到本地
    end
    rect rgb(255, 245, 230)
        Note over C,S: 后续访问
        C->>S: GET /page2.html + Cookie: id=1678
        Note over S: 查数据库,识别用户
        S->>C: 个性化响应
    end
组件说明
响应头 Set-Cookie服务器在响应中设置 Cookie
请求头 Cookie客户端在后续请求中携带 Cookie
客户端本地存储浏览器把 Cookie 存在本地文件中
服务器端数据库服务器根据 Cookie 值查找用户信息

隐私问题:Cookie 让网站可以追踪用户行为——你访问了哪些页面、停留了多久、买了什么。这也是为什么现在有 GDPR 等法规要求网站征得用户同意后才能设置追踪 Cookie。

10.5 Web 缓存(代理服务器)

Web 缓存(也叫代理服务器)是代替源服务器响应 HTTP 请求的中间实体

graph LR
    C["客户端"] -->|"① 请求"| Cache["Web 缓存\n(代理服务器)"]
    Cache -->|"② 缓存未命中时\n转发请求"| Origin["源服务器"]
    Origin -->|"③ 响应"| Cache
    Cache -->|"④ 响应\n(命中时直接返回)"| C
优势说明
减少响应时间缓存通常在本地网络,延迟远小于去源服务器
减少流量不需要每次都跨越 Internet 获取
减轻服务器负载源服务器不需要处理所有请求

条件 GET(Conditional GET)

缓存怎么知道自己存的内容是否过期?通过 If-Modified-Since 头部:

sequenceDiagram
    participant Cache as Web 缓存
    participant Origin as 源服务器
    Cache->>Origin: GET + If-Modified-Since: <上次获取时间>
    alt 资源未修改
        Origin->>Cache: 304 Not Modified(用缓存的)
    else 资源已更新
        Origin->>Cache: 200 OK + 新内容
    end

Memory Cache(内存缓存)
浏览器 RAM · 本次会话 · 纳秒级
Private
存在哪浏览器进程的内存里 生命周期标签页关闭就消失 速度几乎零延迟,比磁盘快 100 倍以上 典型内容当前页面已加载的图片、脚本、样式
你在一个页面里,同一张图片被引用了三次——浏览器只会真正下载一次,剩下两次直接从内存读。关掉标签页,全部消失,下次重开还是要重新加载。
Cache Hit → 不发任何网络请求 Cache Miss → 查 Disk Cache
参考:MDN HTTP Caching ↗
💾
Disk Cache(磁盘缓存)
浏览器硬盘 · 跨会话 · 毫秒级
Private
存在哪本机硬盘(Chrome 在 ~/.cache/google-chrome 生命周期由 HTTP 响应头控制,可跨会话存活 容量通常几百 MB,浏览器自动淘汰旧的 典型内容图片、字体、JS、CSS 等静态资源
服务器通过响应头告诉浏览器"这个文件可以缓存多久",下次访问时浏览器先查磁盘,没过期就直接用,根本不发请求。
Cache-Control: max-age=86400, public
ETag: "abc123def"
Last-Modified: Tue, 14 Apr 2026 10:00:00 GMT
未过期 → 200 from cache,0 字节传输 已过期 → 发条件请求验证
参考:MDN 缓存验证 ↗
🔧
Service Worker Cache
可编程拦截层 · 离线也能用 · 开发者完全控制
Private · PWA
本质一段运行在后台的 JS,拦截所有网络请求 能力自定义缓存策略,甚至离线时返回缓存内容 典型场景PWA(渐进式 Web 应用)、离线地图、新闻 App
这是浏览器缓存里最强大的一层——开发者可以用代码决定"这个请求是去网络取还是返回缓存,或者两者都要然后比对"。断网了?只要 Service Worker 缓存过,页面照样能打开。
浏览器请求
SW 拦截
自定义逻辑
缓存 or 网络
参考:Workbox 缓存策略 ↗
🏢
Forward Proxy Cache(正向代理缓存)
公司 / 学校网关 · 为一批用户服务
Shared
部署位置局域网出口,用户侧 谁在用同一网络下的所有用户共享 典型场景企业内网、学校机房、ISP 运营商
公司里 100 个员工同时打开同一个新闻页面——没有代理缓存,服务器收到 100 个请求;有了代理缓存,第一个人的请求结果被缓存,其余 99 个人直接从代理取,服务器只看到 1 个请求。节省带宽是它的核心价值。
⚠️ HTTPS 普及后,正向代理缓存能缓存的内容大幅减少——加密内容代理根本看不懂
参考:Wikipedia Forward Proxy ↗
🌐
Reverse Proxy / CDN(反向代理 / 内容分发网络)
Cloudflare、Akamai、AWS CloudFront · 服务器侧
Shared · 最关键
部署位置服务器前面,靠近用户的边缘节点 核心价值就近服务,把内容"搬"到离用户近的地方 谁控制网站运营者(不是用户)
没有 CDN:北京用户访问部署在美国的服务器,延迟 200ms+。有了 CDN:Cloudflare 在北京有节点,静态资源从北京节点返回,延迟 10ms。
北京用户
北京 CDN 节点
✓ 命中 or
回源到美国
Cache-Control: public, max-age=31536000
CDN-Cache-Control: max-age=86400 # 只控制 CDN
Surrogate-Control: max-age=3600 # Varnish 等
参考:Cloudflare Cache-Control ↗
🗄️
Server-Side Cache(服务端缓存)
Redis · Memcached · 数据库查询缓存
Server
存在哪服务器内存(Redis)或应用进程内 缓存什么数据库查询结果、渲染好的 HTML 片段、API 响应 核心价值减少数据库压力,加速动态内容生成
这一层跟 HTTP 协议本身关系不大,但它是整个缓存链最后的防线。微博热搜的数据,不可能每次都查数据库——Redis 把结果缓存几秒钟,几亿次请求打过来,数据库纹丝不动。
参考:Wikipedia Cache Stampede ↗

10.6 缓存策略速记(把图和请求头对应起来)

策略关键响应头典型结果适用对象
强缓存(Fresh Cache)Cache-Control: max-age=... / Expires未过期时不发请求,直接本地读取指纹静态资源(JS/CSS/图片)
协商缓存(Validation Cache)ETag + If-None-MatchLast-Modified + If-Modified-Since发送条件请求;未变化则 304,变化则 200 + 新内容HTML、可能变化的 API 响应
共享缓存控制public / private / s-maxage / Vary控制代理/CDN 能否缓存以及缓存多久CDN 与反向代理场景
禁止缓存Cache-Control: no-store每次都走网络,避免落盘登录态、支付页、敏感信息

一次完整的请求旅程

你打开 example.com,浏览器按这个顺序查找资源:

flowchart LR
  A[Memory Cache] --> B[Disk Cache] --> C[Service Worker]
  C --> D[Forward Proxy] --> E[CDN] --> F[服务端缓存] --> G[数据库]

两个最容易混淆的概念

概念容易混淆点正确理解风险提示
Private vs Shared“都是缓存,应该都能共享”浏览器缓存是私有缓存(Private),CDN/代理缓存是共享缓存(Shared)带用户信息的页面必须 Cache-Control: private,否则可能发生用户串数据
验证 vs 直接用“过期就一定要重下”过期后可先做条件请求验证,不等于内容真的变了配好 ETag / Last-Modified 后,大量请求可走 304 Not Modified,正文 0 字节

一个最常见的协商缓存请求长这样:

GET /app.js HTTP/1.1
If-None-Match: "abc123def"
If-Modified-Since: Tue, 14 Apr 2026 10:00:00 GMT

如果资源没变,服务器返回:

HTTP/1.1 304 Not Modified

最容易踩的坑:新版本上线后仍在用旧缓存

这是 CDN 配置里非常常见的事故:代码已经发布新版本,但用户还在命中旧缓存。

最稳的解法是 Cache Busting(内容哈希文件名):

实践里通常这样配:

一个便于自查的顺序:

  1. 先看 DevTools 的 Size / Status,判断是 memory、disk、304 还是网络直出。
  2. 再看响应头里 Cache-ControlETagLast-ModifiedAge
  3. 最后看请求头是否带了 If-None-MatchIf-Modified-Since,确认是否走协商缓存。

如果要做一个稳妥的站点默认配置,可以从这组思路起步:


十一、HTTP 版本演进

版本年份关键特性
HTTP/1.01996非持久连接,每个对象一个 TCP 连接
HTTP/1.11997持久连接(默认)、流水线、Host 头部
HTTP/22015二进制分帧、多路复用、服务器推送、头部压缩
HTTP/32022基于 QUIC(UDP 之上),解决了 TCP 层面的队头阻塞

HTTP/2 的多路复用:HTTP/1.1 的流水线虽然允许连续发送请求,但响应必须按顺序返回(队头阻塞)。HTTP/2 把每个请求/响应分成小的"帧",不同请求的帧可以交错传输,彻底解决了应用层的队头阻塞。

HTTP/3 为什么用 UDP? 因为 TCP 的可靠传输机制会导致传输层的队头阻塞——即使只有一个包丢了,TCP 也会阻塞所有后续包直到重传成功。QUIC 基于 UDP,自己在应用层实现可靠传输,每个流独立,一个流丢包不影响其他流。

点击任意版本,看它改了什么、为什么改。
初代 HTTP/0.9 1991
一行请求,一行响应,纯朴到令人感动
😰 那个年代:互联网只是学术圈的玩具,Tim Berners-Lee 想让科学家方便地共享 HTML 文档
它能做什么?
只有一个方法:GET
请求:GET /index.html(就这一行!)
响应:直接返回 HTML 文本,传完关连接
连接模型
客户端
建连
GET
← HTML
断开
参考:Wikipedia HTTP/0.9 ↗
进化 HTTP/1.0 1996
网页开始有图片了,光靠 GET 不够用了
💥 痛点:Web 爆炸式普及,网页开始包含图片、表单。0.9 连状态码都没有,出错了你也不知道为什么
✨ 新增:Headers(头部信息)、状态码、POST/HEAD 方法、Content-Type 让服务器能返回图片等非 HTML 内容
每次请求都要重新握手(大问题!)
请求1
TCP握手
GET
← 响应
关连接
请求2
TCP握手
GET
← 响应
关连接
一个有 30 张图的网页 = 建立 31 次 TCP 连接 😱
参考:MDN HTTP/1.x 连接管理 ↗
成熟 HTTP/1.1 1997 / 2014修订
统治互联网将近 20 年的主力协议
💥 痛点:1.0 每次都重建连接太慢;随着 Web 变复杂,网页请求数量暴增
✨ 杀手锏:持久连接(Keep-Alive),一条 TCP 连接可以发多个请求,不用每次握手!
持久连接:请求完不断开,继续用
管线化(Pipelining):可以连续发请求,不用等上一个回来(理论上)
Host 头:让一台服务器能托管多个域名(虚拟主机)
队头阻塞(HOL Blocking):第一个请求卡住了,后面全排队等
持久连接 vs 队头阻塞
同一连接
GET1
⏳等响应1
←R1
GET2
←R2
浏览器的解法:同时开 6 条 TCP 连接(治标不治本)
参考:MDN 队头阻塞 ↗
安全 HTTPS(HTTP + TLS) 2000s 起普及
明文传输密码?那简直是在裸奔
💥 痛点:HTTP 明文传输,网络上的任何人都能看到你的密码、银行账号、私信……
✨ 在 HTTP 下面加一层 TLS(传输层安全协议),对所有内容加密。URL 从 http:// 变成 https://
加密:中间人看到的是乱码
身份验证:证书确保你连的真的是 google.com,而不是假冒者
TLS 握手增加了额外延迟(TLS 1.3 大幅改善了这点)
参考:Cloudflare TLS Handshake ↗
革命 HTTP/2 2015
彻底重构底层,告别文本,拥抱二进制多路复用
💥 痛点:1.1 时代网页平均请求数超过 100 个,队头阻塞 + 重复 Header 让性能触底。Google 做了个叫 SPDY 的实验,证明可以更快
✨ 核心突破:多路复用(Multiplexing)——一条 TCP 连接里并行传多个请求,互不阻塞!
二进制分帧:告别纯文本,数据切成"帧",更高效解析
多路复用:同一连接并行传输,彻底解决 HTTP 层的队头阻塞
Header 压缩(HPACK):重复 Header 不再重复传,节省带宽
服务器推送:服务器可以主动推送 CSS/JS,不用等浏览器来要
TCP 层的队头阻塞仍然存在(丢一个包,整条连接都要等)
多路复用:同一连接并行
流 1
帧1-1
帧1-2
← 响应1
流 2
帧2-1
帧2-2
← 响应2
流 3
帧3-1
← 响应3
交错传输,谁先到谁先用,互不干扰 🎉
参考:RFC 9113(HTTP/2)↗
未来 HTTP/3 2022 正式标准化
换掉 TCP,用 QUIC 从根子上解决丢包问题
💥 痛点:HTTP/2 解决了应用层队头阻塞,但底层 TCP 丢一个包还是全部卡住——在移动网络、高延迟场景下这很致命
✨ 彻底换底座:抛弃 TCP,用 QUIC(基于 UDP 重新实现的可靠传输协议),每条流独立,丢包只影响那一条流!
QUIC 传输:基于 UDP,但有可靠性保证,在用户态实现,可以快速迭代
0-RTT 连接:老朋友再次连接,几乎零额外延迟,直接发数据
连接迁移:从 WiFi 切到 4G,连接不断(手机用户的福音!)
内置 TLS 1.3:安全不是选项,是标配
丢包隔离:只影响丢包的那条流
流 1
包A
包B
包C ✓
流 2
丢包⚡
重传…
恢复 ✓
流 3
包X
包Y
包Z ✓
流 2 丢包重传,流 1 和流 3 完全不受影响 ✌️
参考:RFC 9000(QUIC)↗

十二、DNS 体系与解析机制(可视化速记)

DNS 不是一个中心化的数据库,它是一棵覆盖全球的分布式树,没有任何一台服务器知道所有答案。
根服务器
全球只有 13 个根服务器地址(a.root-servers.net 到 m.root-servers.net),但背后有 1800+ 台物理机器(Anycast)。它们只知道一件事:".com 去找谁问,.cn 去找谁问"
TLD 服务器
顶级域名服务器。.com 的 TLD 由 Verisign 管理,.cn 由 CNNIC 管理。它们知道:google.com 的权威服务器是谁
权威服务器
每个域名所有者自己维护的服务器(或委托给 Cloudflare、AWS Route 53 等)。它存着 最终答案:google.com = 142.250.x.x
本地解析器
你的 ISP 或公司网络分配给你的 DNS 服务器(如 8.8.8.8 是 Google 的,1.1.1.1 是 Cloudflare 的)。它替你去跑完整条查询链,然后把结果缓存下来
参考:IANA Root Servers ↗
你以为 DNS 是“查一下电话簿”,其实是一场最多跑 4 个人 的接力赛。
1
浏览器检查自己的 DNS 缓存(访问过的域名缓存在内存里) -> 命中直接用
2
操作系统缓存(macOS/Windows 有系统级 DNS 缓存) -> 命中直接用
3
/etc/hosts 文件(本地静态映射,优先级最高) -> 命中直接用
4
都没有? -> 发给本地解析器(你的 ISP 的 DNS 或 8.8.8.8)
5
本地解析器若没缓存 -> 问根服务器:"google.com 谁管?" -> 根服务器说:"去问 .com TLD"
6
.com TLD 服务器:"google.com 谁管?" -> TLD 说:"去问 ns1.google.com"
7
google.com 权威服务器:"www.google.com 的 IP 是?" -> 终于得到答案
8
本地解析器把结果缓存,返回给你的电脑
全程你的电脑只和本地解析器说话,其余步骤都是本地解析器代劳的,这叫递归查询。本地解析器去问各层服务器的过程叫迭代查询
参考:Cloudflare Recursive DNS ↗
DNS 不只是存“域名 -> IP”,它是一个通用的分布式数据库,存着各种类型的记录:
A google.com. 142.250.80.46 # 域名 -> IPv4 地址(最常用) AAAA google.com. 2607:f8b0::200e # 域名 -> IPv6 地址 CNAME www.google.com. google.com. # 域名 -> 别名(别名链) MX google.com. smtp.google.com. # 邮件服务器地址 NS google.com. ns1.google.com. # 权威服务器是谁 TXT google.com. "v=spf1 ..." # 任意文本(反垃圾邮件、域名验证) SOA google.com. ns1.google.com. ... # 区域权威信息
CNAME 不能用在裸域名(apex domain)上,你不能把 google.com 设为另一个域名的 CNAME,只有 www.google.com 这样的子域名才行。这是 DNS 的历史限制,也引出了 CNAME flattening 等技术。
参考:Cloudflare CNAME Flattening ↗
DNS 查询慢(最多要问 4 层服务器),所以每个节点都会缓存结果。控制缓存时间的字段叫 TTL(Time To Live),单位秒。
google.com. 300 IN A 142.250.80.46 # TTL = 300 秒,5 分钟后这条记录过期,需要重新查询
TTL 长
(如 86400 = 1天)查询快,缓存命中率高,DNS 服务器压力小。缺点:改 IP 后,全球缓存要等 TTL 过期才能更新,你改了服务器 IP,一天内部分用户还在访问旧 IP
TTL 短
(如 60 = 1分钟)IP 改动传播快,灵活。缺点:每分钟全球都要重新查询一次,DNS 服务器压力大,用户首次查询慢
工程技巧:准备切换服务器前,提前把 TTL 从 1天降到 60秒,等旧 TTL 过期后再改 IP,改完等 60 秒后再把 TTL 恢复 1天。这样切换几乎零感知。
参考:DNS Cache Poisoning 与 DNSSEC ↗
回到开头的问题:北京和纽约访问 google.com,DNS 返回的 IP 不一样,这就是 DNS 在 CDN 中的核心作用。
1
北京用户的 DNS 请求,经过北京的本地解析器,带着解析器的 IP 发到 Google 的权威 DNS
2
Google 的权威 DNS 看到请求来自中国 IP -> 返回最近的中国 CDN 节点 IP
3
纽约用户同理 -> 返回美国东海岸 CDN 节点 IP
这叫 GeoDNS / Anycast DNS。权威 DNS 服务器根据请求来源地理位置,动态返回不同 IP,让用户自动路由到最近节点。这是 CDN “就近访问”的核心机制,发生在 DNS 层。
一个隐藏的精度问题
GeoDNS 是根据本地解析器的 IP 判断位置,而不是你的终端 IP。如果你用了远端公共 DNS,权威 DNS 可能判断偏差并返回更远节点。EDNS Client Subnet 通过传递客户端网段信息提升命中精度。
参考:RFC 7871(EDNS Client Subnet)↗
Split-Horizon DNS
同一个域名,在公司内网和公网返回不同 IP。内网用户访问 api.company.com 走内网地址,外部用户走公网地址。VPN 连上公司后解析结果突然变化,常见原因就是它。
DNS 做负载均衡
一个域名可配置多个 A 记录,DNS 服务器轮流返回(Round Robin DNS)。客户端通常先用第一个地址,从而把流量分散到多台服务器。
DNS over HTTPS
传统 DNS 是明文 UDP,运营商可观察查询域名。DoH(DNS over HTTPS)把 DNS 查询封装进 HTTPS,加密传输。
DNS 污染
对特定域名返回伪造 IP,导致访问失败或被错误重定向。这类问题常与 DNS 劫持、缓存投毒一起被讨论。
参考:DNS Spoofing / Hijacking ↗

十三、关键术语速查

英文中文一句话解释
Client-Server客户端-服务器永远在线的服务器响应客户端的请求
Peer-to-Peer (P2P)对等网络没有固定服务器,节点之间直接通信
Self-scalability自扩展性P2P 中每个新节点既带来需求也带来供给
Socket套接字应用进程与传输层之间的 API 接口
Port Number端口号标识主机上具体进程的数字
Well-known Port知名端口0-1023,保留给标准服务
Stateless无状态HTTP 服务器不记忆之前的请求
CookieCookie为无状态 HTTP 添加用户状态的机制
Persistent Connection持久连接一个 TCP 连接传输多个对象
Pipelining流水线不等响应就连续发送多个请求
Web Cache / ProxyWeb 缓存/代理代替源服务器响应请求,减少延迟和流量
Conditional GET条件 GET用 If-Modified-Since 检查缓存是否过期
RTT往返时延分组一来一回的时间
TLS传输层安全在 TCP 之上提供加密和身份认证
QUIC快速 UDP 互联网连接基于 UDP 的传输协议,HTTP/3 的基础
Tit-for-Tat以牙还牙BitTorrent 的激励机制,上传多才能下载快
Rarest First最稀有优先BitTorrent 优先下载副本最少的块


0 / 2000
Loading comments...