程云来 / 杭州
返回文章·计算机网络
·约 15 分钟

网络面试(二十二):请求为什么先到代理和 CDN?

沿着浏览器到订单服务的部署路径,区分正向/反向代理、L4/L7 负载均衡与 CDN,再检查缓存键、回源 TLS、真实客户端地址和流式转发。


浏览器连上的,可能不是订单服务器

浏览器访问 shop.example.com,页面需要公共商品图片和自己的订单 O204 详情。DNS 返回的地址可能属于 CDN 边缘入口;图片可以从边缘缓存返回,订单请求则继续经过源站入口,转给某台订单实例。

这条路径里既有“决定去哪里”,也有“终止连接、读取请求、建立下一跳”,还有“能否直接提供已存响应”。代理描述代谁转发,负载均衡描述如何选后端,CDN 描述分布式内容交付;同一个系统可以同时承担多种职责。

下面用一份教学部署说明关系:CDN 终止客户端 TLS,回源再使用 HTTPS;源站入口承担反向代理和 L7 负载均衡,后接 S1、S2。它不是当前博客的生产拓扑,也不代表所有 CDN 的配置。

正向和反向,是站在哪一边看?

概念谁使用它代为通信浏览器面对的目标常见职责
正向代理客户端侧选择或配置代理仍想访问原目标站点,由代理代转出口访问控制、统一转发
反向代理服务端部署一个对外入口访问公开站点入口,后端由入口选择路由、TLS 终止、缓冲、缓存
负载均衡把工作分配给多个可用目标取决于部署在连接层还是应用层按策略选实例、健康检查
CDN通过分布式节点交付站点内容通常仍使用原站点公开域名边缘交付、缓存、回源及其他服务

在 HTTP 术语里,proxy 通常指客户端选择的消息转发代理,gateway 常指表现得像源站、再转发到后端的反向代理。方向描述角色关系,不是报文只向某个方向移动;响应也要返回客户端。RFC 9110 §3.7:中间节点

HTTPS 经正向代理时,常用 CONNECT 建立到目标主机端口的隧道。若代理只转发隧道字节,浏览器与目标之间的 TLS 可以保持端到端,代理并不因此获得 HTTP 正文;若部署了 TLS 检查并让客户端信任代理签发的证书,则是另一种终止与信任模型。不能把“使用了代理”直接等同于“代理能解密”。RFC 9110 §9.3.6:CONNECT

这次请求沿哪条路径走?

正在绘制图表…
查看 Mermaid 源码
flowchart TB
    B["浏览器:shop.example.com"]
    E["CDN 边缘:终止客户端 TLS"]
    G["源站入口:反向代理 / L7 负载均衡"]
    S1["订单实例 S1"]
    S2["订单实例 S2"]
    B -->|客户端 HTTPS| E
    E -.->|公共资源缓存命中,返回浏览器| B
    E -->|需要回源:另一条 HTTPS| G
    G -->|示意:内网 HTTP| S1
    G -->|示意:内网 HTTP| S2

Mermaid 大图

可滚动查看图表,点击缩放比例可恢复 100%。按 Esc 关闭。

图中的 S1、S2 是备选目标,一次普通请求选择其中一个,不是复制到两台同时执行;其余响应沿对应路径返回。公共图片只有在缓存匹配、可复用且无需回源验证时,才走命中分支;订单详情采用绕过共享缓存的策略。

浏览器验证的是公开站点 shop.example.com 的服务身份。CDN 回源建立另一条连接,有自己的目标地址、SNI、证书验证和 HTTP 目标配置,不能沿用浏览器“已经验证成功”的结论。图中入口到实例的 HTTP 是明文示意;若该跳需要保密和对端认证,应按实际边界配置 TLS,而不是凭“内网”两个字推断安全。

CDN 的回源模式会影响保护范围。例如 Cloudflare 的 Full (strict) 模式要求回源 HTTPS 并验证证书条件,这是具体产品模式,不是所有 CDN 自动具备的默认保证。Cloudflare:Full (strict)

每一跳也可以使用不同 HTTP 版本。浏览器到边缘是 h3,不要求边缘到源站也必须 h3;终止节点可以重新编码下一跳消息。排查时记录“哪一跳的协议”,比只说“站点开启 HTTP/3”准确。

L4 与 L7:选择依据有什么差别?

L4 负载均衡主要按传输层的连接/流信息选择目标,例如地址、端口、协议和连接状态;L7 则理解 HTTP 等应用协议,可以根据 Host、路径或字段路由。产品可能同时支持 TLS 监听或其他能力,不能把层级标签当作所有实现细节。

比较项典型 L4 分发典型 HTTP L7 分发
选择依据连接/流特征、可用目标与分发策略Host、路径等请求信息与分发策略
后端选择粒度通常一个连接/流绑定一个目标可以按请求或流选择后端
是否需理解 HTTP不以解析 HTTP 路径为必要条件需要读懂相应应用协议
已建立长连接不把普通连接中途无缝拆给任意新实例也不能自动迁移已建立的 WebSocket 业务状态

AWS Network Load Balancer 与 Application Load Balancer 分别提供连接层和应用层分发的具体例子;它们的功能不能直接推广为所有负载均衡器的共同默认。AWS NLB、AWS ALB

L7 入口终止客户端连接、建立后端连接时,一条客户端 HTTP/2 连接上的不同请求可以送往不同实例。因此“客户端一直没断开”不能证明它一直访问 S1。

轮询按次分配,加权分配体现配置容量,最少连接倾向连接较少的目标,哈希尝试让相同键落到相同目标。它们使用的是不同信号:连接少不必然 CPU 轻,IP 相同可能是很多用户经过同一个 NAT。实际策略要结合请求成本与测量。NGINX:HTTP 负载均衡

健康检查同样有范围。端口能接入或 /health 返回 200,只说明所检查的条件,不自动证明订单数据库和依赖全部正常。粘性会话可以改善实例亲和性,但实例故障、扩缩容或客户端地址改变后仍可能换目标;登录状态和第 21 章的事件历史不能只依赖“总会回到 S1”。

CDN 怎样让用户到达边缘?

站点可以通过 DNS 记录把公开域名指向 CDN 服务,由服务进行地址选择;也可结合 Anycast,让多个节点对外宣告相同地址,由网络路由决定流量落点。两者分别影响名字解析结果与数据包路径,可以一起使用。

Anycast 的选择受路由策略和网络拓扑影响,不能保证地图上直线距离最近的节点,也不能保证每次都选延迟最低的节点。DNS 缓存、递归解析器位置、节点负载和故障策略也会影响结果。Cloudflare:Anycast 网络、RFC 4786 §3.1

到达边缘之后,边缘才判断有没有可用响应。缓存未命中、需要验证或明确绕过缓存时,继续访问源站或上层缓存,通常称为回源。CDN 不只是“把域名解析快一点”,也不只处理图片;动态请求可能仍经过边缘,但是否缓存是另外的决定。

缓存键错了,比没命中更危险

第 16 章解释了新鲜度和验证,现在还要确认:本次请求拿到的已存响应,属于哪个目标和哪种表示? HTTP 缓存键至少涉及请求方法和目标 URI,并在匹配时考虑 Vary 指定的字段;具体 CDN 还可提供自定义键规则。RFC 9111 §2/§4.1

考虑公共图片接口 /image?sku=42&width=400。教学键保留公开 scheme、authority、path、全部 query,再按表示选择规则匹配;这里只为看出冲突,不规定某家 CDN 的默认键或通用规范化算法。

请求差异如果把差异从键/匹配条件中删除正确判断
width=400 与 width=1200小图可能被当成大图返回width 改变内容,应区分
sku=42 与 sku=43返回另一商品的图片sku 改变内容,应区分
shop.example.com 与另一个主机多站点内容可能混用目标 authority 应正确区分
表示由 Accept-Encoding 决定压缩与未压缩表示可能混用正确处理对应 Vary/平台表示规则
仅有统计参数 utm_source 不同保留时可能产生重复缓存只有确认不影响表示与策略,才考虑归并

“删掉所有 query 能提高命中率”会把 width、sku 一起删掉。排序 query、大小写或百分号归一化,也必须与源站解释一致:源站可能把重复参数顺序看成有意义,边缘却把它们合并。Cloudflare 文档明确提供 query 的包含/排除选项,说明这是可配置语义,不能凭扩展名猜测。Cloudflare:缓存键

同一 URI 的个人订单详情还可能随登录者不同而变化。最简单的教学策略是订单 API 在边缘绕过缓存,响应明确限制保存,例如 Cache-Control: private, no-store;公共图片另设可共享策略。private 与 no-store 的含义不同,这个组合明确表达该响应不进入共享或私有 HTTP 缓存,而不是用无限多用户键来补救一个本应私有的接口。

请求有 Cookie 或响应有 Set-Cookie,不是协议层面的通用禁止缓存开关;不同平台可能另有保守默认,但应该显式检查规则。Authorization 的共享缓存限制也有规范条件,不该粗略归纳为“只要登录就永远不会缓存”。RFC 9111 §3/§7.3:存储条件与敏感信息

更新源站内容也不会立刻删除所有边缘旧副本。可以等待新鲜期、验证、按平台能力清理,或为不可变公共资源使用带版本/内容标识的新 URL;实际失效范围与传播需验证。清 CDN 缓存不等于清浏览器缓存,反过来也一样。

动手追踪命中与回源

先比较400px和1200px公共图片,再切换个人订单。省略width会让不同图片撞上同一键;回源验证失败则会在另一个边界停止。

同样到达边缘,这次请求还会走多远?

浏览器缓存未命中。边缘已有一份新鲜400px公共图片;个人订单明确绕过共享缓存。改变请求与匹配规则,看结果和经过的节点。

GET /image?sku=42&width=400

本次匹配键:GET https://shop.example.com/image?sku=42&width=400

固定相同Host、表示条件与sku,仅width可能变化。
浏览器 → 边缘
尚未建立
边缘 → 源站入口
无需回源
入口 → 实例
未发送
1 / 3

教学部署,无实际CDN或负载均衡。假定边缘终止客户端TLS,回源HTTPS,入口到实例为内网HTTP示意;不推断内网安全。所有键规则为本例配置,省略失效传播、重试与多层缓存。

“真实客户端 IP”为什么不能随便信?

S1 的网络连接对端可能是源站代理,代理的对端又可能是 CDN。为了传递前面的信息,可以使用标准 Forwarded,或常见的 X-Forwarded-For、X-Forwarded-Proto 等字段。例如:

http
Forwarded: for=192.0.2.43;proto=https;host=shop.example.com

这是教学字段,地址使用文档保留范围。for 表示代理报告的客户端信息,proto/host 表达相关的原始请求信息;它们不是密码学身份证明。客户端也可能直接送来伪造字段,沿途节点也可能修改。RFC 7239 §5/§8.1

建议先定义哪些直接连接来源属于可信代理,再由入口覆盖/清理外部同名信息,按约定解析可信链。不要一律取 X-Forwarded-For 最左值,也不要信任所有网段。NGINX realip 的可信来源和递归规则就是具体实现入口,实际配置必须与代理链一致。NGINX realip

proto 如果被错误解释成 http,应用可能生成错误跳转、Cookie 属性或回调地址;Host 若未经验证就用于绝对 URL,也可能把请求者提供的名字带进业务链接。信任代理解决的是来源可信度,登录鉴权和订单授权仍然要由应用完成。IP 还可能因 NAT 或移动网络变化,不能作为稳定的用户身份。

SSE 和 WebSocket 经过代理,哪里会卡住?

SSE 需要每个转发环节及时把响应字节送出去。应用 flush 不保证代理也立即转发;关闭缓存不等于关闭缓冲。以 NGINX 为例,proxy_buffering 决定响应缓冲行为,X-Accel-Buffering 可参与控制,但能否生效还取决于代理配置。NGINX:proxy_buffering

超时也要看定义。NGINX 的 proxy_read_timeout 约束相邻两次读取之间的等待,不是整个响应总时长;链路中其他网关还可能设置总连接时长或另外的空闲限制。第 21 章的心跳策略要覆盖实际链路,不能只把一个参数调大。NGINX:proxy_read_timeout

经典 HTTP/1.1 WebSocket 代理需要处理 Upgrade。Connection 与 Upgrade 属于逐跳机制,不会因为客户端发送了就自动原样跨过所有代理;NGINX 的对应代理方式需要显式设置相关转发条件。HTTP/2/3 的扩展 CONNECT 另按支持情况处理。NGINX:WebSocket 转发

重连后落到 S2 时,如果历史只存在 S1 的内存,Last-Event-ID 再正确也可能无处补回。这是共享历史或快照恢复的职责,不是粘性连接能永久保证的问题。

故障出在哪一跳,先保留什么证据?

现象先定位的边界能缩小范围的证据
图片还是旧版浏览器、边缘、源站的缓存分别判断响应 Age/ETag、平台命中标记、实际 URL
公共图片快,订单慢缓存命中与动态回源路径不同回源连接耗时、入口与实例处理记录
某些用户读到错误内容缓存键、Vary、授权与绕过规则两个身份请求、相同键是否错误复用
SSE 整批出现或定时断开缓冲层与各跳超时应用发送时间、各跳收到/转发时间
502/504 来自入口入口到上游的响应或等待产生响应的节点、上游地址与错误记录

502 通常表示网关收到无效上游响应,504 表示等待上游及时响应失败;仅凭状态码不能断定数据库是哪一步坏了。平台可能有额外错误页和状态约定,需要结合产生它的节点。一次请求标识和时间线应能跨入口与后端关联,避免把边缘返回时间当成业务开始时间。

30—60 秒参考回答

正向代理代表客户端转发,反向代理是服务端对外入口。负载均衡负责选目标,L4 通常按连接/流信息,L7 可以读 HTTP 的 Host、路径等请求信息。CDN 用分布式节点交付内容,命中可减少回源,动态请求也可能经过边缘。缓存必须检查方法、目标、query、表示和身份条件,个人响应不能误共享。TLS 终止后下一跳要独立验证,客户端 IP 字段也只应按可信代理链解释。最后按每一跳检查协议、缓存、缓冲、超时和上游记录,才能定位问题。

四个追问及解析

1. CDN 和反向代理是互斥概念吗?

不是。CDN 的边缘节点可以作为站点反向代理,同时做缓存与转发;反向代理也可能只有一个机房入口。要按职责和部署范围描述,而不是给每台机器只贴一个名字。

2. L4 负载均衡能按 /orders 路径分流吗?

单纯传输层分发不解析 HTTP 路径。按路径路由属于应用层理解,HTTPS 场景还需在适当节点终止 TLS 或由能理解应用的系统处理;产品叠加能力要具体说明。

3. 为什么清了 CDN 缓存,浏览器还是旧图片?

浏览器可能仍使用自己的新鲜缓存,未向边缘发送请求;也可能命中了其他 URL 或另一层缓存。先确认实际请求与响应来源,再验证清理范围。内容版本化 URL 能减少对同步清理的依赖。

4. 代理收到订单请求后自动重试,更安全吗?

不一定。上游可能已提交但响应丢失,再转给 S2 可能重复创建。代理重试条件必须结合方法、请求能否重放及应用幂等契约;换实例不能取消第 14 章的提交不确定性。

两个容易说错的结论

“HTTPS 是加密的,所以 CDN 不能读请求。” 如果 CDN 是 TLS 终止节点,它会解密当前连接并处理 HTTP;纯隧道转发才是另一种关系。两者取决于具体终止与信任配置。

“所有 GET 都能放到 CDN,同一路径就是同一份内容。” GET 不保证公开可共享,同一路径还可能随 query、Host、表示或身份变化。缓存条件与键必须一起审查。

请求跨了多条连接和多个处理节点,单个“总耗时”已经解释不了哪里慢。把 DNS、连接、TLS、首字节、正文与每跳处理拆成证据,才能让排障从猜测变成逐步缩小范围。

资料核验日期:2026-10-09。依据 IETF HTTP/Forwarded 规范与 AWS、NGINX、Cloudflare 官方资料;拓扑、字段和缓存键均为教学设定,未部署实际 CDN 或代理集群。