proxy - gateway - load - balancer - cdn
11. 代理、网关、负载均衡与 CDN
0. 本章先解决什么问题
真实网络服务通常不是“一台客户端直接访问一台服务器”这么简单。
用户访问一个域名,背后可能经过:
浏览器
-> DNS
-> CDN 边缘节点
-> 负载均衡器
-> API 网关
-> 反向代理
-> 多个应用实例
-> 下游依赖
这条链路里,代理、网关、负载均衡、CDN 都可能改变请求路径、Header、缓存行为、超时、来源 IP、错误码。
本章要解决:
- 这些中间层分别解决什么问题。
- 它们怎样组织多个服务成为一个统一入口。
- 502、503、504、来源 IP 丢失、缓存旧内容等问题为什么经常出现在这些层。
- 排障时怎样判断问题发生在哪一段。
这张图怎么读
这张图把入口链路画成一条可排查路径:CDN、负载均衡、网关、反向代理、应用实例、下游依赖。每一层都可能终止 TLS、改 Header、缓存、限流、重试、设置超时或直接返回错误码。
1. 为什么不能只有一台服务直接暴露
如果所有用户都直接访问一台机器,会遇到:
| 问题 | 解释 |
|---|---|
| 单点故障 | 机器挂了,服务整体不可用 |
| 扩展困难 | 流量增长时无法分摊 |
| 安全边界弱 | 应用实例直接暴露在外部网络 |
| 证书和协议处理重复 | 每个实例都要处理 TLS、压缩、限流等 |
| 路由混乱 | 不同路径、不同版本、不同服务难以统一入口 |
| 地理距离远 | 用户离源站远,延迟高 |
中间层的价值是把外部访问和内部服务组织分开。
外部用户看到一个稳定入口;
内部可以有多个实例、多个区域、多种服务。
2. 代理:代替一方发送或接收请求
代理的核心是:它站在通信路径中,代替某一方和另一方交互。
2.1 正向代理
正向代理更靠近客户端,代表客户端访问外部资源。
Client -> Forward Proxy -> Internet Server
客户端知道自己在使用代理,目标服务看到的可能是代理地址。
常见用途:
| 用途 | 说明 |
|---|---|
| 访问控制 | 控制客户端能访问哪些外部资源 |
| 审计 | 记录访问行为 |
| 缓存 | 缓存外部资源 |
| 网络出口统一 | 让内部网络通过统一出口访问外部 |
2.2 反向代理
反向代理更靠近服务端,代表内部服务接收外部请求。
Client -> Reverse Proxy -> Internal Service
客户端访问的是代理入口,不一定知道背后有多少实例。
常见用途:
| 用途 | 说明 |
|---|---|
| TLS 终止 | 代理处理 HTTPS,内部可走其他协议 |
| 路由转发 | 根据域名、路径、Header 转到不同服务 |
| 压缩 | 对响应做压缩 |
| 限流 | 控制请求速率 |
| 安全过滤 | 拦截异常请求 |
| 统一日志 | 记录入口访问 |
3. 网关:统一入口和协议/策略边界
网关通常比普通反向代理承担更多应用级策略:
Client -> Gateway -> Service A
-> Service B
-> Service C
常见能力:
| 能力 | 作用 |
|---|---|
| 路由 | 按路径、域名、版本把请求转到不同服务 |
| 鉴权 | 在入口验证身份或权限 |
| 限流 | 防止某些客户端打爆系统 |
| 协议转换 | 外部 HTTP,内部可能是另一种协议 |
| 统一错误 | 将内部错误包装成统一响应 |
| 观测 | 记录日志、指标、追踪信息 |
网关是系统边界。它的配置错误会让请求根本到不了目标服务,或者让正确请求被错误拒绝。
4. 负载均衡:把流量分给多个实例
负载均衡器的目标是:
一个入口,多台实例,共同承载流量。
4.1 四层和七层负载均衡
| 类型 | 工作层次 | 依据 | 特点 |
|---|---|---|---|
| 四层 LB | 传输层 | IP、端口、连接 | 不理解 HTTP 语义,转发成本低 |
| 七层 LB | 应用层 | Host、Path、Header、Cookie | 能做内容路由和应用策略 |
四层更像连接分发,七层更像理解请求后再路由。
4.2 调度策略
常见策略:
| 策略 | 含义 |
|---|---|
| 轮询 | 依次分配到各实例 |
| 加权轮询 | 性能强的实例分更多 |
| 最少连接 | 分给当前连接少的实例 |
| 哈希 | 按客户端或请求特征稳定分配 |
| 一致性哈希 | 节点变化时减少重新映射 |
不同策略适合不同负载形态。请求耗时差异很大时,简单轮询可能不均衡。
4.3 健康检查
负载均衡不能只知道实例存在,还要知道实例是否健康。
健康检查可能包括:
端口是否开放
HTTP 健康路径是否返回成功
依赖是否可用
实例负载是否过高
如果健康检查过于简单,可能把“端口开着但服务不可用”的实例继续纳入流量;如果检查过于严格,可能把短暂波动误判为故障。
边界条件:健康检查成功不等于真实请求成功
健康检查通常是一个简化请求。它能证明“某个探测路径正常”,但不能证明所有真实请求都正常。
| 健康检查只证明 | 仍可能失败的真实路径 |
|---|---|
| 端口能连接 | 应用线程池已满,业务请求排队 |
/health 返回成功 | 真实接口依赖的数据库/缓存/外部服务失败 |
| 本机进程存活 | 某个租户、区域、版本或路由配置错误 |
| 轻量请求很快 | 大请求、上传、长连接、流式响应超时 |
| 单实例可用 | 实例之间配置不一致、灰度版本异常 |
这会制造一种典型故障:
LB 认为实例健康
真实用户请求持续 5xx 或超时
解决思路不是把健康检查无限复杂化,而是分层设计:
- 存活检查:进程是否还活着
- 就绪检查:是否能接真实流量
- 深度检查:关键依赖是否可用
- 业务指标:真实请求错误率和延迟
健康检查是流量入口的控制信号,不是完整的业务正确性证明。
5. CDN:把内容放到离用户更近的地方
CDN 的核心是边缘缓存和就近访问。
User -> Nearby CDN Edge -> Origin
如果边缘节点有缓存:
User -> CDN Edge -> 返回缓存内容
如果没有缓存:
User -> CDN Edge -> Origin
<- Origin Response
<- 缓存并返回
CDN 常用于:
| 内容 | 适配原因 |
|---|---|
| 图片、脚本、样式 | 静态资源,缓存收益大 |
| 下载文件 | 大流量,适合边缘分发 |
| 视频切片 | 用户分布广,带宽需求高 |
| 可缓存接口 | 读多写少,缓存策略清晰 |
CDN 也可能做 TLS、压缩、安全过滤、访问控制,但它的核心价值仍然是减少距离和源站压力。
6. 一次请求穿过中间层
一个典型路径:
Client
-> DNS 解析到 CDN 或入口
-> CDN 边缘节点
-> 入口负载均衡
-> 网关
-> 反向代理
-> 应用实例
每一层都可能:
终止 TLS
修改 Header
设置超时
缓存响应
记录日志
丢弃异常请求
返回错误码
重试上游
所以同一个 HTTP 状态码,可能不是应用直接返回的。比如:
| 状态码 | 可能来源 |
|---|---|
| 403 | CDN 安全策略、网关权限、应用权限 |
| 404 | 网关路由不存在、应用路由不存在、CDN 缓存了旧路径 |
| 502 | 代理连接上游失败、上游响应格式异常 |
| 503 | 负载均衡无健康实例、服务过载 |
| 504 | 网关等待上游超时 |
7. 来源 IP 和转发 Header
经过代理后,应用直接看到的连接来源可能是代理 IP,而不是用户真实 IP。
User IP -> Proxy IP -> App
应用看到 TCP 对端: Proxy IP
为了传递原始来源,代理常加入 Header:
X-Forwarded-For: client, proxy1, proxy2
X-Forwarded-Proto: https
X-Forwarded-Host: example.com
但这些 Header 有信任边界。客户端也可以伪造同名 Header,所以应用不能无条件相信。
正确思路:
代码块收起展开
只信任来自可信代理添加或规范化后的转发头;
在入口处清理外部传入的伪造转发头;明确记录代理链路和真实来源解析规则。
8. 超时和重试:中间层最容易制造“偶发问题”
每一层都有超时:
客户端超时
CDN 超时
负载均衡超时
网关超时
反向代理超时
应用超时
下游依赖超时
如果它们配置不协调,就会出现复杂现象。
例如:
网关 30s 超时
应用 60s 才放弃下游
结果:
网关 30s 返回 504
应用还在继续占用资源
重试也一样。客户端、代理、网关都可能重试。没有幂等设计时,重试可能导致重复操作。
机制深挖:超时要按链路从内到外收敛
如果中间层很多,超时配置要有顺序感。一个实用原则是:越靠近内部依赖,越早失败;越靠近用户入口,超时略长一些,用来接收内部明确失败,而不是抢先断开。
一个错误配置:
客户端: 20s
网关: 30s
应用: 60s
下游依赖: 55s
结果可能是:
客户端 20s 已经放弃
网关和应用还在占资源
下游依赖继续工作
最后结果无人接收
更合理的方向是:
下游依赖超时 < 应用超时 < 网关超时 < 客户端可等待时间
这不是固定数字规则,而是因果关系:内部越早给出明确失败,外层越能释放连接、返回错误、记录证据。否则一次慢下游会被放大成入口连接堆积、应用线程占满、重试风暴和 504。
9. 联系实际:502、503、504 怎么定位
这三个状态码经常来自中间层:
| 状态码 | 直觉 | 常见检查 |
|---|---|---|
| 502 | 网关和上游通信异常 | 上游端口、协议、响应格式、连接被重置 |
| 503 | 服务不可用 | 是否无健康实例、过载、限流、维护 |
| 504 | 等上游超时 | 上游处理慢、网络慢、超时配置不匹配 |
排查顺序:
- 确认错误由哪一层返回:CDN、LB、网关、代理、应用。
- 查该层访问日志和上游日志是否有同一个请求。
- 判断请求有没有到达应用实例。
- 如果没到,查路由、健康检查、连接、TLS、协议。
- 如果到了,查应用处理时间和下游依赖。
- 检查超时、重试、连接池、空闲连接回收配置。
只看应用日志很可能漏掉入口层问题;只看入口层也可能看不到应用内部耗时。
排障卡:先判断错误码是谁返回的
入口链路长了以后,502/503/504/403/404 不一定来自应用。排障第一件事是定位“响应生成层”。
| 证据 | 说明 |
|---|---|
| 响应 Header | 可能带 CDN、网关、代理标识 |
| CDN/LB/网关访问日志 | 判断请求是否经过该层,以及上游耗时 |
| 应用访问日志 | 判断请求是否真正到达实例 |
| Trace/request id | 把入口层和应用层日志串起来 |
| 上游连接日志 | 判断代理到实例是连接失败、超时还是协议异常 |
| 缓存命中标记 | 判断是否根本没回源 |
一个实用判断:
入口层有日志,应用无日志
-> 问题多半在路由、健康检查、连接、TLS、限流、缓存或代理到上游之间
应用有日志,入口层返回 504
-> 看应用处理是否超过网关超时,或应用是否还在继续占用资源
这能把“服务挂了”拆成“哪一层认为上游不可用”。
10. 学完本章你能解决什么问题
学完本章,你应该能:
- 区分正向代理、反向代理、网关、负载均衡、CDN 的核心职责。
- 解释四层负载均衡和七层负载均衡的区别。
- 理解健康检查、调度策略、超时、重试如何影响可用性。
- 判断来源 IP 为什么会变成代理 IP,以及转发 Header 的信任边界。
- 分析 CDN 缓存旧内容、命中异常、源站压力变化等问题。
- 排查 502、503、504 时判断错误来自入口层、代理层、应用层还是上游依赖。
- 画出一个外部请求穿过 CDN、LB、网关、代理、实例的路径,并为每一层列出证据。