第10章 L7 代理:协议感知与基于内容的路由
学习目标
- 掌握 L7"请求级"路由相对 L4"连接级"的能力跃迁与代价
- 理解基于内容路由的维度(Host/Path/Header/Method/Cookie)与匹配优先级陷阱
- 搞清 L7 的能力全家桶:改写、应用健康检查、重试、sticky、缓冲
- 明白重试为什么对非幂等请求危险,sticky 为什么比 L4 源哈希精确
前置知识
- 第09章 L4 代理(对照:连接级 vs 请求级)
- 第04章 HTTP 代理协议、第13章 Nginx(本章原理的组件落地)
原理
请求级:L7 的根本能力
L7 代理把字节流完整解析成应用层消息(第02章),因此能在每个请求粒度上做决策——这是与 L4"连接级"最根本的区别:
一条 keep-alive 连接:
请求1 GET /api/users ──▶ 后端A(api 集群)
请求2 GET /static/x.js ──▶ 后端B(静态集群) ← 同一条连接,不同请求去不同后端
请求3 POST /api/order ──▶ 后端A
正因如此,L7 天然解决了 第09章 的"长连接负载不均"——它在请求级重新分发,gRPC/h2 的多个 stream 能打散到不同后端。
基于内容路由的维度
L7 能读到完整请求,于是可按多种维度路由:
| 维度 | 例子 | 典型用途 |
|---|---|---|
| Host | api.example.com vs web.example.com | 多站点/多服务 |
| Path | /api/* vs /static/* | 微服务拆分、动静分离 |
| Header | X-Canary: true、User-Agent | 灰度、设备分流 |
| Method | GET 走缓存、POST 走主库 | 读写分离 |
| Query/Cookie | ?ver=2、session=... | A/B 测试、会话保持 |
路由匹配的优先级陷阱
多条规则同时匹配时,谁优先?这是 L7 配置最常见的坑。一般规则:精确 > 前缀(长的优先)> 正则(按定义顺序)。Nginx 的 location 匹配顺序尤其反直觉:
location = /api/health { ... } # ① 精确匹配,最高优先
location ^~ /api/static/ { ... } # ② 前缀且阻止后续正则
location ~ \.php$ { ... } # ③ 正则,按顺序
location /api/ { ... } # ④ 普通前缀,最长匹配优先
location / { ... } # ⑤ 兜底
头号事故:以为
location /api/会拦住/api/health,但若另有location = /api/health或正则先命中,路由就跑偏了。改路由必测全部样例 URL。
L7 能力全家桶(L4 都做不到)
一旦解析到应用层,能做的事爆炸式增长:
- 改写:
rewrite/redirect、改 Header、改 Path、加删 Cookie - 应用层健康检查:
GET /healthz看返回码/响应体,比 L4 的"端口通"精确得多 - 重试:后端失败自动重试另一个后端(注意幂等性,见下)
- 超时分级:连接超时、首字节超时、整体超时
- 熔断/限流:错误率高就熔断、按 QPS 限流(第31章)
- 缓存/压缩:缓存响应、gzip/br 压缩
- 鉴权:JWT 校验、外部鉴权(auth_request)
- 灰度:按 Header/权重把流量切给新版本
- 可观测:每请求埋点、access log、分布式追踪注入(第30章)
这些正是 API 网关(apiGateway 手册)和 Service Mesh 数据面(第29章)的能力来源——它们本质都是"被编排的 L7 反向代理"。
upstream / cluster:后端组抽象
L7 代理把一组后端抽象成 upstream(Nginx)/ cluster(Envoy),绑定 LB 算法 + 健康检查:
upstream api {
后端列表 + 权重
LB 算法(rr/least-conn/ip-hash/一致性哈希)
主动健康检查(/healthz)
连接池/keepalive
}
会话保持:L7 的 cookie sticky 比 L4 源哈希精确
第09章 的 L4 用"源 IP 哈希"做会话保持,但NAT 后大量用户共享一个出口 IP(公司、运营商),源哈希会把他们全压到一个后端。L7 能种一个 cookie 标识后端,精确到用户:
首次请求 → 选后端A → 响应里 Set-Cookie: SRV=a
后续请求带 Cookie: SRV=a → L7 直接送后端A
重试的危险:幂等性
L7 自动重试很香,但对非幂等请求是灾难:
POST /pay 扣款 → 后端处理成功但响应丢失/超时 → 代理重试 → 又扣一次!
GET/PUT/DELETE幂等,可安全重试POST非幂等,默认不应重试,或只在"连接都没建立"这种确定没副作用时重试- Nginx
proxy_next_upstream默认不含non_idempotent,别手贱打开
代价:L7 比 L4 重一个数量级
能力的另一面是开销:
- 要完整解析+缓冲应用报文(CPU、内存)
- 每连接维护应用层状态
- 改写、压缩、鉴权都吃 CPU
- 不能像 L4 那样 splice 零拷贝(第12章)——数据必须进用户态
选型心法(呼应 第02章):只在需要"看内容"时才上 L7。纯转发、极致性能用 L4;要路由/改写/治理才上 L7。很多架构是 L4(边缘抗量)+ L7(内层治理) 分层组合。
️ 实现 / 命令
实验一:Nginx L7 内容路由 + 动静分离 + 改写
upstream api_pool {
least_conn;
server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
keepalive 32; # 到后端的连接池
}
server {
listen 80;
location = /healthz { return 200 "ok\n"; } # 精确匹配优先
location /api/ {
proxy_pass http://api_pool;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_next_upstream error timeout; # 只对安全情况重试,不含 non_idempotent
}
location /static/ {
proxy_pass http://10.0.0.20:8081; # 静态走另一组
proxy_cache_valid 200 10m; # L7 才能做的缓存
}
rewrite ^/old/(.*)$ /new/$1 permanent; # L7 才能做的路径改写
}
实验二:按 Header 做灰度(金丝雀)
# 带 X-Canary: true 的请求进新版本,其余进稳定版
map $http_x_canary $pool {
"true" canary_pool;
default stable_pool;
}
server {
listen 80;
location / { proxy_pass http://$pool; }
}
curl -H "X-Canary: true" http://localhost/ # → 新版本
curl http://localhost/ # → 稳定版
这种"按请求内容切流量"是 L4 永远做不到的,也是灰度发布的基础(详见 apiGateway · 灰度发布)。
实验三:cookie 会话保持
upstream app {
server 10.0.0.11:8080;
server 10.0.0.12:8080;
sticky cookie SRV expires=1h; # 商业版/第三方模块;开源可用 ip_hash 退而求其次
}
排错
| 现象 | 根因 | 解决 |
|---|---|---|
| 路由跑到错误的 location | 匹配优先级理解错 | 按"精确>^~前缀>正则>前缀"核对,测全部样例 URL |
| POST 被重复执行(重复下单) | 对非幂等请求开了重试 | 关闭 non_idempotent 重试 |
| 灰度 Header 不生效 | 改写/map 顺序或缓存命中 | 检查 map、绕过缓存验证 |
| sticky 失效,会话乱跳 | 用了 L4 源哈希且用户共享出口 IP | 改 L7 cookie sticky |
| 改写后 301 循环 | rewrite 规则自我匹配 | 加 break/精确条件 |
后端拿到的 Host 不对 | 没设 proxy_set_header Host | 显式透传或改写 Host |
| 大响应内存暴涨 | 缓冲了大 body | 关 proxy_buffering 走流式(第03章) |
本章小结
- L7 在请求级决策,天然解决长连接负载不均,能按 Host/Path/Header/Method/Cookie 路由。
- 路由优先级陷阱(精确>前缀>正则)是头号事故源,改路由必测全样例。
- L7 能力全家桶(改写/应用健康检查/重试/sticky/缓存/灰度/可观测)是 API 网关与 Mesh 数据面的根基。
- 重试要看幂等性(POST 别重试);cookie sticky 比 L4 源哈希精确;代价是重一个数量级、不能零拷贝。
下一章 第11章 透明代理,回到内核——客户端零配置时,iptables/TPROXY/eBPF 是怎么把流量"拐"进代理的,这也是 Istio Sidecar 零侵入劫持的底层原理。