HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 代理技术全栈手册

    • 代理技术全栈手册 - HiHuo
    • 原理篇

      • 第01章 代理是什么:正向 / 反向 / 透明 / 隧道的统一模型
      • 第02章 代理与网络层级:L3 / L4 / L5 / L7 在哪里截断流量
      • 第03章 一个请求穿过代理的一生:连接生命周期全景
    • 协议篇

      • 第04章 HTTP 代理协议:绝对 URI、CONNECT 隧道、转发头与连接池
      • 第05章 HTTPS 与 TLS 代理:终止 / 透传 / MITM / SNI / mTLS
      • 第06章 SOCKS 协议:SOCKS4/4a/5 与 UDP ASSOCIATE 报文级解析
      • 第07章 HTTP/2、gRPC 与 HTTP/3(QUIC) 代理的挑战
      • 第08章 代理自动配置:PAC / WPAD / 系统代理 / NO_PROXY
    • 层级与转发篇

      • 第09章 L4 代理:TCP/UDP 转发与连接级负载均衡
      • 第10章 L7 代理:协议感知与基于内容的路由
      • 第11章 透明代理:iptables REDIRECT/DNAT、TPROXY 与 eBPF 劫持
      • 第12章 数据搬运的艺术:splice / sendfile / 零拷贝 / io_uring
    • 组件横评篇

      • 第13章 Nginx / OpenResty:反向代理、upstream 与 Lua 可编程
      • 第14章 HAProxy:L4/L7、ACL、健康检查与 stick table
      • 第15章 Envoy:xDS 动态配置与 filter chain,为何是云原生数据面
      • 第16章 Traefik / Caddy:自动服务发现与自动 HTTPS
      • 第17章 Squid 与正向/缓存代理:企业出网、缓存与审计
      • 第18章 mitmproxy:抓包、改包、脚本化调试
      • 第19章 内网穿透与隧道:frp / gost / SSH 隧道 / ngrok
      • 第20章 科学上网生态的技术原理(技术中立)
    • 多语言手写篇

      • 第21章 Go:100 行手写 HTTP/CONNECT + SOCKS5 代理
      • 第22章 Rust:基于 tokio 的高性能 TCP 代理
      • 第23章 Python:asyncio 实现,适合调试与脚本
      • 第24章 C:epoll 裸写与零拷贝,及语言选型对比
    • 容器与K8s篇

      • 第25章 Docker 里的代理:HTTP_PROXY、build/pull 与 daemon 配置
      • 第26章 Sidecar 与流量劫持:Istio init-container 的 iptables 原理
      • 第27章 Ingress 与南北流量:Ingress-nginx 与 Gateway API
      • 第28章 Egress 与出网治理:出口网关、registry mirror、审计
      • 第29章 Service Mesh 数据面:Envoy Sidecar 全链路
    • 进阶篇

      • 第30章 可编程代理:Lua / Wasm / eBPF / xDS,代理的"软件定义"
      • 第31章 性能调优:并发模型、连接池、超时与重试、压测
      • 第32章 排错决策树:502 / 504 / 握手失败 / 环路 / 泄漏
      • 第33章 代理安全:开放代理、SSRF、凭证泄漏与攻击面
    • 底层机制篇

      • 第34章 代理的背压与流控:一个代理最难的部分
      • 第35章 socket 与 TCP 状态机:半关闭、超时、连接生命周期
      • 第36章 HTTP/2 帧、流控与 HPACK:h2 代理的内部机制
      • 第37章 负载均衡算法推导与韧性状态机
      • 第38章 Capstone:把玩具代理改造成生产级骨架
    • 综合实战篇

      • 第39章 企业多跳转发链:拓扑、协议矩阵与贯穿性难题
      • 第40章 端到端实战:把 6 类流量全代理通
      • 第41章 更刁钻的流量:gRPC、长轮询、WebRTC、大文件、双向流
      • 第42章 可落地完整参考实现:一套能跑的多协议转发栈
    • 附录

      • 附录 A:代理协议报文速查(HTTP / SOCKS / PAC / PROXY protocol)
      • 附录 B:组件选型决策树
      • 附录 C:抓包与命令速查

第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 能读到完整请求,于是可按多种维度路由:

维度例子典型用途
Hostapi.example.com vs web.example.com多站点/多服务
Path/api/* vs /static/*微服务拆分、动静分离
HeaderX-Canary: true、User-Agent灰度、设备分流
MethodGET 走缓存、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 零侵入劫持的底层原理。

Prev
第09章 L4 代理:TCP/UDP 转发与连接级负载均衡
Next
第11章 透明代理:iptables REDIRECT/DNAT、TPROXY 与 eBPF 劫持