网关与反向代理
原始素材:
7-基础设施与运维/7.07-gateway-proxy.md
在高并发的互联网架构中,如何把流量安全、高效地送到正确的服务? 反向代理解决"流量怎么分发",API网关解决"请求怎么处理"。本文通过真实案例(前台接待、保安系统、智能路由)深入理解网关的设计哲学和工程实践。
1. 为什么要"网关"?
1.1 从一个真实案例说起:某电商的架构演进
某电商平台在业务快速增长时遇到了严重的架构问题:
场景还原:
阶段一:直接暴露服务
客户端 → 直接调用用户服务、订单服务、支付服务...
↓
问题1:服务IP暴露,存在安全隐患
问题2:无法统一做认证、限流
问题3:新增服务需要修改客户端配置
⚠️ 直接暴露的致命问题
- 安全隐患 : 所有服务IP暴露,容易被攻击
- 功能重复 : 每个服务都要做认证、限流、日志
- 扩展困难 : 新增服务要修改所有客户端
- 协议混乱 : 有的用HTTP,有的用gRPC,客户端要适配
改进后的架构(引入网关):
客户端 → API网关(Nginx/Kong) → 内部服务
↓
统一认证、限流、路由
↓
客户端只知道网关地址
✨ 改进后的效果
- 安全 : 真实服务IP隐藏,只有网关对外
- 功能收敛 : 认证、限流、日志在网关统一处理
- 扩展容易 : 新增服务只需网关配置路由
- 协议统一 : 对外HTTP,内部可用gRPC
1.2 网关的生活化比喻
前台接待
想象你去一家大公司:
- 没有前台 : 访客直接找各部门,不知道在哪,公司乱成一团
- 有前台 : 访客先到前台,前台问清楚来意,再引导到对应部门
API网关就是系统的"前台" :
- 反向代理 : 前台,引导访客到正确的部门
- API网关 : 智能前台,还能检查访客身份(认证)、限制访问人数(限流)
一句话区分:正向代理是"客户端的代理",反向代理是"服务器的代理"。
用户(浏览器)→ 访问域名 → 反向代理(Nginx)→ 负载均衡 → 后端集群(Web1 | Web2 | Web3)
-
客户端无感知,只需要访问域名
-
隐藏真实服务器架构,统一对外接口
-
提供负载均衡、安全防护、SSL卸载等功能
-
典型代表:Nginx、HAProxy、AWS ELB
-
网站需要承载高并发流量(负载均衡)
-
统一HTTPS证书管理(SSL卸载)
-
防护DDoS攻击和SQL注入
-
灰度发布、A/B测试、蓝绿部署
"反向代理 = 代理服务器" —— 客户端不知道真实服务器,只知道域名
2. 什么是反向代理?
2.1 正向代理 vs 反向代理
正向代理(Forward Proxy) :
- 部署在客户端侧
- 代替客户端访问外部资源
- 典型应用:VPN、翻墙工具
- 例子:公司网络,你通过代理访问外网
反向代理(Reverse Proxy) :
- 部署在服务器端
- 接收客户端请求并转发给内部服务
- 客户端只知道代理存在,不知道真实服务器
- 例子:Nginx、HAProxy
对比表:
| 维度 | 正向代理 | 反向代理 |
|---|---|---|
| 部署位置 | 客户端侧 | 服务器端 |
| 服务对象 | 客户端 | 服务器 |
| 典型应用 | VPN、翻墙 | 负载均衡、网关 |
| 透明性 | 服务器看到代理IP | 客户端看到代理IP |
| 目的 | 隐藏真实客户端、加速访问 | 隐藏真实服务器、负载均衡 |
2.2 反向代理的核心价值
价值一:负载均衡
将流量分发到多个后端服务器,避免单点过载。
客户端
↓
Nginx(反向代理)
↓
┌─────────┬─────────┬─────────┐
│ 服务器1 │ 服务器2 │ 服务器3 │
└─────────┴─────────┴─────────┘
价值二:安全防护
隐藏真实服务器IP,防止直接攻击。统一在代理层做安全防护。
客户端 → 只能看到Nginx的IP
真实服务器 → 只在内网,外部无法直接访问
价值三:SSL终结
在代理层处理HTTPS加密解密,后端服务用HTTP,降低后端计算开销。
HTTPS客户端 → Nginx(加密/解密) → HTTP后端服务
↑
SSL终结点
3. Nginx:为什么能扛起百万并发?
3.1 Master-Worker进程模型
Nginx采用多进程 架构,而不是多线程:
Master进程(管理者) :
- 负责读取和验证配置文件
- 管理Worker进程(启动、停止、重新加载)
- 不处理具体请求
Worker进程(工作者) :
-
实际处理HTTP请求
-
每个Worker是独立的进程,相互隔离
-
数量通常设置为CPU核心数,避免上下文切换开销
-
隔离性好 : 一个Worker崩溃,不影响其他Worker
-
充分利用多核 : 每个Worker独立运行
-
避免多线程复杂性 : 无需处理锁、竞争等问题
3.2 事件驱动 + 异步非阻塞
这是Nginx高性能的核心秘密:
传统Apache(多进程/线程模型) :
- 一个连接 = 一个进程/线程
- 并发数受限于系统进程/线程数
- 大量连接时,进程切换开销巨大
Nginx(事件驱动模型) :
- 使用epoll(Linux)/kqueue(macOS)等高效I/O多路复用机制
- 一个Worker进程可以同时处理数万个连接
- 连接没有数据时,不会占用CPU,有新数据时通过事件通知唤醒
生活化比喻
- Apache : 餐厅里每个顾客配一个服务员(进程),顾客多需要大量服务员
- Nginx : 一个超级服务员,同时服务所有顾客,谁需要服务就去谁那里,而不是一直站在某个顾客旁边
Master-Worker 进程模型 + 事件驱动 = 高性能的秘诀
Nginx 进程架构:Master 进程管理所有 Worker,负责配置加载和平滑升级。
Master 进程
├── Worker 1(处理 0 请求)
├── Worker 2(处理 0 请求)
├── Worker 3(处理 0 请求)
└── Worker 4(处理 0 请求)
💡 事件驱动:一个 Worker 同时处理数万个连接
| 传统 Apache | VS | Nginx |
|---|---|---|
| 一个连接 = 一个进程/线程 | 事件驱动 + 异步非阻塞 | |
| ❌ C10K 问题 | ✅ 百万并发 |
Worker 数量 = CPU 核心数 (通常设置为 auto,让 Nginx 自动检测) 太多了上下文切换开销大,太少了无法利用多核性能。
4. 什么是API网关?
4.1 为什么需要API网关?
想象一个没有网关的系统:
- 客户端需要知道多个服务的地址(用户服务、订单服务、支付服务...)
- 每个服务都要自己做认证、限流、日志
- 协议不统一,有的用HTTP,有的用gRPC
- 服务升级时,客户端也需要跟着改
⚠️ 没有网关的问题
- 客户端复杂 : 需要配置多个服务地址
- 功能重复 : 每个服务都要实现认证、限流
- 协议混乱 : 客户端要适配多种协议
- 升级困难 : 服务升级,客户端也要改
有了API网关之后:
- 客户端只需要知道网关地址,网关负责路由到正确服务
- 认证、限流、日志等横切逻辑统一在网关处理
- 网关可以做协议转换,对外统一暴露HTTP
- 后端服务升级,只需要改网关配置,客户端无感知
想象成写字楼的「前台」——所有访客都要先经过这里,才能到达不同的办公室
API 网关路由 — 统一入口,分发请求:
客户端(来访者)
│
▼
🔐 API 网关(统一入口 + 认证)
│
├── /api/users → 用户服务
├── /api/orders → 订单服务
└── /api/pay → 支付服务
统一校验用户身份,无需每个后端服务都写登录逻辑。支持 JWT、OAuth2、API Key 等多种认证方式。
示例:用户请求携带 JWT Token → 网关校验签名和过期时间 → 通过后将用户 ID 添加到请求头 → 转发给后端服务。
| 功能需求 | 没有网关 (直接访问) | 有 API 网关 |
|---|---|---|
| 身份认证 | 每个服务都要写一遍登录校验 | ✅ 统一在网关层校验 JWT |
| 限流保护 | 每个服务自己实现限流 | ✅ 网关统一限流,保护后端 |
| 协议转换 | HTTP、gRPC、WebSocket各自处理 | ✅ 网关统一对外暴露 HTTP |
| 灰度发布 | 需要改负载均衡器配置 | ✅ 网关层按 Header 路由 |
4.2 API网关的核心功能
| 功能 | 说明 | 典型场景 |
|---|---|---|
| 路由转发 | 根据URL、Header等规则,将请求转发到不同服务 | /api/users → 用户服务,/api/orders → 订单服务 |
| 负载均衡 | 同一个服务有多实例时,分摊流量 | 用户服务有3台实例,轮询分发请求 |
| 认证鉴权 | 统一校验JWT、OAuth Token | 未登录用户无法访问/api/admin |
| 限流熔断 | 控制流量上限,防止服务被压垮 | 每秒最多1000请求,超过返回429 |
| 协议转换 | 对外HTTP,内部可转gRPC | 客户端用HTTP,网关转gRPC调用内部服务 |
| 灰度发布 | 按Header或比例,将部分流量导到新版本 | 5%用户体验新版本,95%用旧版本 |
| 日志监控 | 统一记录请求日志,便于分析和排障 | 记录每次请求的耗时、状态码、返回大小 |
5. 网关实战:如何构建完整的网关架构?
5.1 完整架构图
┌───────────────────────────────────────────────────────────────────────┐
│ 客户端(浏览器/APP) │
└───────────────────────────┬─────────────────────────────────────────┘
│ HTTPS
▼
┌───────────────────────────────────────────────────────────────────────┐
│ 外层:CDN + WAF │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ CDN(内容分发网络) │ │
│ │ - 静态资源缓存(图片、CSS、JS) │ │
│ │ - 就近访问,降低延迟 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ WAF(Web应用防火墙) │ │
│ │ - 防护SQL注入、XSS攻击 │ │
│ │ - 拦截恶意Bot、爬虫 │ │
│ │ - CC攻击防护 │ │
│ └───────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────┐
│ 中层:API网关(Nginx/Kong) │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 第一层:SSL终结 + 安全防护 │ │
│ │ - HTTPS / TLS 1.3 │ │
│ │ - HSTS、安全响应头 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 第二层:认证与鉴权 │ │
│ │ - JWT Token校验 │ │
│ │ - OAuth 2.0 / SSO集成 │ │
│ │ - API Key管理 │ │
│ │ - 权限校验(RBAC) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 第三层:流量控制 │ │
│ │ - 限流- 令牌桶/漏桶算法 │ │
│ │ - 熔断- 防止故障扩散 │ │
│ │ - 降级- 服务不可用时的备用方案 │ │
│ │ - 灰度发布- 按比例分配流量 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 第四层:路由与负载均衡 │ │
│ │ - 路径路由- Path-based Routing) │ │
│ │ - 域名路由- Host-based Routing) │ │
│ │ - Header路由- Header-based Routing) │ │
│ │ - 负载均衡算法- 轮询/加权/最少连接/IP哈希) │ │
│ │ - 服务发现- Service Discovery)集成 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 第五层:协议转换与数据处理 │ │
│ │ - SSL终结- HTTPS ↔ HTTP) │ │
│ │ - 协议转换- HTTP ↔ gRPC / WebSocket) │ │
│ │ - 请求/响应转换- JSON ↔ XML) │ │
│ │ - 数据压缩- Gzip / Brotli) │ │
│ │ - 缓存- Cache)- 静态资源和API响应 │ │
│ └───────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────┐
│ 内层:微服务集群 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 用户服务 │ │ 订单服务 │ │ 商品服务 │ │ 支付服务 │ │
│ │ User Svc │ │ Order Svc │ │ Product Svc │ │ Payment Svc │ │
│ │ │ │ │ │ │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └────────────────┴────────────────┴────────────────┘ │
│ │ │
│ 服务发现与配置中心/ etcd) │
│ - 服务注册与发现 │
│ - 健康检查 │
│ - KV配置存储 │
└───────────────────────────────────────────────────────────────────────┘
5.2 路由与负载均衡
网关的核心职责之一,就是把请求送到正确的地方 。这涉及两个关键能力:路由(去哪台服务器)和负载均衡(怎么分配流量)。
路由规则:从URL到服务
想象一个电商系统,不同的URL对应不同的服务:
/api/users/*→ 用户服务/api/orders/*→ 订单服务/api/products/*→ 商品服务/api/pay/*→ 支付服务
Nginx配置示例:
server {
listen 80;
server_name api.example.com;
# 用户服务
location /api/users/ {
proxy_pass http://user-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 订单服务
location /api/orders/ {
proxy_pass http://order-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 商品服务
location /api/products/ {
proxy_pass http://product-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 支付服务(需要更高安全级别)
location /api/pay/ {
# 限制IP访问
allow 10.0.0.0/8;
deny all;
proxy_pass http://payment-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
负载均衡:四种策略对比
当同一个服务有多个实例时,如何选择?
| 策略 | 原理 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 轮询 | 按顺序依次分配给每台服务器 | 服务器性能相近 | 简单公平 | 不考虑服务器当前负载 |
| 加权轮询 | 按权重比例分配,权重高的分配更多 | 服务器性能不均 | 充分利用高性能服务器 | 需要合理设置权重 |
| 最少连接 | 分配给当前连接数最少的服务器 | 长连接场景、视频流 | 动态适应负载变化 | 需要实时统计连接数 |
| IP哈希 | 根据客户端IP计算哈希,同一IP永远分配到同一台服务器 | 需要会话保持 | 保证会话一致性 | 某个IP流量大时会造成单点压力 |
Nginx配置示例:
upstream backend_weighted {
server 10.0.1.10:8080 weight=3; # 性能好,承担更多流量
server 10.0.1.11:8080 weight=2;
server 10.0.1.12:8080 weight=1; # 性能差,承担较少流量
}
upstream backend_least_conn {
least_conn;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
upstream backend_ip_hash {
ip_hash;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
⚖️ 负载均衡:把"压力"均匀分摊到多台服务器
想象成银行的取号系统——把客户均匀分配到各个窗口,避免某个窗口排长队
负载均衡策略:轮询 | 加权轮询 | 最少连接 | IP Hash | 一致性哈希
以「轮询」为例:按照服务器列表顺序,依次将请求分配给每台服务器。就像银行叫号,1 号窗口完事到 2 号,轮着来。
服务器状态(共 4 台):
| 服务器 | 负载占比 | 权重 | 请求数 |
|---|---|---|---|
| Server-A | 33% | 2 | 0 |
| Server-B | 12% | 1 | 0 |
| Server-C | 17% | 2 | 0 |
| Server-D | 38% | 5 | 0 |
集群概览:
| 总请求 | 待处理 | 平均负载 | 最高负载 | 负载标准差 | 最忙服务器 |
|---|---|---|---|---|---|
| 0 | 0 | 25% | 38% | 10.8 | Server-D |
6. 网关安全:如何守护系统大门?
6.1 认证与鉴权
传统方式(每个服务各自认证):
- 用户服务、订单服务、支付服务...每个都要校验JWT
- 代码重复,维护困难
- secret分散在各个服务,泄露风险高
网关统一认证:
- 客户端携带Token访问网关
- 网关校验Token合法性(签名、过期时间)
- 校验通过后,将用户信息(如user_id)添加到请求头,转发给后端服务
- 后端服务无需校验,直接从Header获取用户信息
认证在网关,鉴权在服务 :
- 认证 : 你是谁?(校验Token,获取用户身份)
- 鉴权 : 你能做什么?(根据用户角色判断权限)
就像公司前台:前台认证你的身份(身份证),但具体权限由各部门判断。
想象成写字楼门禁——检查工牌、验证身份,没权限的人进不来。
JWT(JSON Web Token)认证流程:
| 步骤 | 角色 | 操作 |
|---|---|---|
| 1 | 用户 | 发起登录请求 |
| 2 | 网关 / Nginx | 转发登录请求到认证服务 |
| 3 | 认证服务 | 验证密码,生成 JWT Token(Header + Payload + Signature) |
| 4 | 用户 / 客户端 | 保存 Token(LocalStorage 或 Cookie) |
| 5 | 后续请求 | 在 HTTP Header 中携带 Authorization: Bearer <Token> |
| 6 | 网关 / Nginx | 校验 Token 签名和过期时间,通过后转发请求 |
| 7 | 后端服务 | 从 Token 中解析用户信息,处理业务逻辑 |
JWT Token 结构(Header.Payload.Signature):
HEADER(Base64)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
→ { "alg": "HS256", "typ": "JWT" }
PAYLOAD(Base64)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
→ { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
SIGNATURE
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
→ HMACSHA256(base64Url(header) + "." + base64Url(payload), secret)
| 对比维度 | Session + Cookie | JWT | OAuth2.0 |
|---|---|---|---|
| 存储位置 | 服务端存储 Session,客户端存 Cookie | 客户端存储 Token,服务端无状态 | 授权服务器存储,客户端存 Access Token |
| 扩展性 | ❌ 需要共享 Session,扩展复杂 | ✅ 无状态,易于水平扩展 | ✅ 分布式架构,支持大规模系统 |
| 安全性 | ⚠️ Cookie 可能被窃取,需要 CSRF 防护 | ⚠️ Token 泄露风险,需 HTTPS + 短期有效 | ✅ 行业最佳实践,支持多种安全机制 |
| 实现复杂度 | 🟢 简单,开箱即用 | 🟡 中等,需要 Token 管理 | 🔴 复杂,需要授权服务器 |
| 适用场景 | 传统 Web 应用、后台管理系统 | SPA、移动端 API、微服务 | 第三方登录、开放平台、SSO |
网关安全最佳实践:
| # | 原则 | 说明 |
|---|---|---|
| 1 | 统一在网关层验证 | 不要在每个微服务里重复写认证逻辑,统一在网关层校验 JWT 或 Session |
| 2 | HTTPS 强制 | 网关层强制 HTTPS,防止 Token 在传输过程中被窃取(中间人攻击) |
| 3 | Token 过期策略 | Access Token 短期有效(15 分钟),配合 Refresh Token 实现无感知续期 |
| 4 | 黑名单机制 | 用户登出或 Token 泄露时,将 Token 加入黑名单(Redis 存储) |
6.2 HTTPS与SSL终结
为什么需要HTTPS?
- 安全 : 防止数据在传输过程中被窃取
- 合规 : 现代浏览器对HTTP网站显示"不安全"警告
- SEO : 搜索引擎优先收录HTTPS网站
SSL终结方案:
-
只在网关层配置HTTPS和证书
-
网关负责TLS握手和加解密
-
网关和后端服务之间使用HTTP明文传输(内部网络可信)
-
后端服务专注于业务逻辑,无需处理TLS
-
简化管理 : 证书只在网关配置,后端无需配置
-
降低开销 : 后端服务不需要处理TLS握手
-
统一更新 : 证书更新只需在网关操作
想象成公司的前台接待——对外使用正式头衔(HTTPS),对内用内部称呼(HTTP),负责"翻译"身份。
SSL 终结流程:
客户端(HTTPS) Nginx(SSL 终结) 后端集群(HTTP)
证书: *.example.com X-Forwarded-For: 203.0.113.42 专注业务逻辑
TLS 1.3 / AES-256-GCM ──→ X-Forwarded-Proto: https ──→ 无需处理 TLS
X-Real-IP: 203.0.113.42
证书申请流程(5 步):
| 步骤 | 操作 | 命令 |
|---|---|---|
| 1 | 生成私钥 | openssl genrsa -out private.key 2048 |
| 2 | 创建 CSR | openssl req -new -key private.key -out csr.pem |
| 3 | 域名验证 | 添加 DNS TXT 记录或上传验证文件 |
| 4 | 签发证书 | 下载 certificate.crt 和 chain.crt |
| 5 | 部署配置 | nginx -t && systemctl reload nginx |
✨ SSL 终结的核心优势:
| 优势 | 说明 |
|---|---|
| ⚡ 性能提升 | TLS 握手和加解密集中在 Nginx 处理,后端专注业务,吞吐量提升 2-5 倍 |
| 🔧 简化运维 | 证书统一管理,只需在 Nginx 配置一次,续期和更换一键完成 |
| 🔒 集中安全 | SSL/TLS 配置统一管控,强制最新协议版本,统一添加 HSTS、CSP 等安全头 |
| 📊 统一监控 | 所有 HTTPS 流量经过 Nginx,统一记录日志、分析握手性能、监控证书有效期 |
7. 限流与熔断:如何防止系统被"流量洪水"冲垮?
7.1 限流算法对比
| 算法 | 核心思想 | 突发流量 | 适用场景 | 实现复杂度 |
|---|---|---|---|---|
| 令牌桶 | 桶里装令牌,有令牌才能通过 | 允许一定程度的突发 | API限流、带宽控制 | 中等 |
| 漏桶 | 请求进桶,匀速流出处理 | 强制平滑,突发会被缓存或拒绝 | 需要严格匀速处理的场景 | 中等 |
| 滑动窗口 | 统计时间窗口内的请求数 | 严格按窗口计数,超出一律拒绝 | 精确统计(如"1分钟内最多100次") | 较高 |
7.2 Nginx限流配置实战
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_zone $server_name zone=server_limit:10m rate=100r/s;
server {
listen 80;
server_name api.example.com;
# 用户服务 - 普通限流
location /api/users/ {
# 应用限流
# burst=20 - 桶容量,允许突发20个请求
# nodelay - 不延迟处理突发请求(立即处理或拒绝)
limit_req zone=mylimit burst=20 nodelay;
# 限制单个IP的连接数
limit_conn addr 10;
proxy_pass http://user-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 订单服务 - 更严格的限流
location /api/orders/ {
# 更严格的限流:每秒5个请求
limit_req_zone $binary_remote_addr zone=order_limit:10m rate=5r/s;
limit_req zone=order_limit burst=10 nodelay;
proxy_pass http://order-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 限流后的处理
# 当请求被限流时,返回429 Too Many Requests
error_page 429 /429.html;
location = /429.html {
internal;
return 429 '{"error": "Too Many Requests", "message": "Rate limit exceeded. Please try again later."}';
add_header Content-Type application/json;
}
}
- 普通接口 : 每秒10个请求,允许突发20个
- 重要接口(支付、订单): 每秒5个请求,允许突发10个
- 全局保护 : 所有请求总和不超过每秒100个
想象成水坝的闸门——控制水流速度,防止下游被淹没
选择限流算法
令牌桶
5 / 10 令牌
⏰ 令牌产生器 (2/秒)
| 维度 | 令牌桶 (Token Bucket) | 漏桶 (Leaky Bucket) | 滑动窗口 (Sliding Window) |
|---|---|---|---|
| 核心思想 | 桶里装令牌,有令牌才能通过 | 请求进桶,匀速流出处理 | 统计时间窗口内的请求数 |
| 突发流量 | ✅ 允许一定程度的突发(桶里有令牌) | ❌ 强制平滑,突发会被缓存或拒绝 | ❌ 严格按窗口计数,超出一律拒绝 |
| 适用场景 | API 限流、带宽控制(允许突发) | 需要严格匀速处理的场景(如消息队列) | 精确统计(如"1分钟内最多100次") |
| 实现复杂度 | 中等 | 中等 | 较高(需要记录每个时间窗口的请求) |
| Nginx 配置 | limit_req_zone (漏桶) | limit_req_zone (漏桶) | 需第三方模块或 Lua |
基础限流连接数限制黑白名单
# 定义限流区域
# $binary_remote_addr: 按 IP 限流
# zone=mylimit:10m: 区域名称和大小
# rate=10r/s: 每秒最多10个请求
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
listen 80;
server_name api.example.com;
location / {
# 应用限流
# burst=20: 桶容量,允许突发20个请求
# nodelay: 不延迟处理突发请求
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://backend;
}
}
- limit_req_zone: 在 http 块中定义限流区域
- $binary_remote_addr: 使用二进制 IP 地址作为限流键(省内存)
- zone=mylimit:10m: 区域名称 mylimit,分配 10MB 内存
- rate=10r/s: 每秒允许 10 个请求(漏桶算法)
- burst=20: 桶的容量为 20,允许一定程度的突发流量
- nodelay: 不延迟处理突发请求(立即处理或拒绝)
7.3 熔断:防止故障扩散
熔断器的工作原理:
- 关闭状态 : 正常转发请求,同时统计错误率
- 开启状态 : 当错误率超过阈值,熔断器开启,直接返回错误,不再转发请求
- 半开状态 : 经过一段时间后,允许少量请求通过试探,如果成功则关闭熔断器
熔断就像电路保险丝 : 电流过大时,保险丝自动熔断,保护整个电路不被烧毁。
类似地,当后端服务出现大量错误时,熔断器"跳闸",快速失败,防止故障扩散到整个系统。
8. 总结:网关设计的核心思维
8.1 核心原则回顾
| 原则 | 含义 | 实践要点 |
|---|---|---|
| 路由 | 把请求送到正确的地方 | 路径路由、域名路由、Header路由 |
| 负载均衡 | 分摊流量到多台服务器 | 轮询、加权、最少连接、IP哈希 |
| 安全 | 守护系统大门 | 认证鉴权、HTTPS、WAF |
| 限流 | 防止被流量冲垮 | 令牌桶、漏桶、滑动窗口 |
| 熔断 | 防止故障扩散 | 快速失败、降级方案 |
| 可观测 | 监控和排障 | 日志、指标、链路追踪 |
8.2 技术选型建议
选择网关:
│
├─ 只需要反向代理、负载均衡?
│ ├─ 是 → Nginx(首选)
│ └─ 否 → 继续
│
├─ 需要丰富的插件生态?
│ ├─ 是 → Kong(基于Nginx)
│ └─ 否 → 继续
│
├─ Spring Cloud 全家桶?
│ ├─ 是 → Spring Cloud Gateway
│ └─ 否 → Nginx
9. 名词速查表
| 名词 | 英文 | 解释 |
|---|---|---|
| 反向代理 | Reverse Proxy | 部署在服务器端,接收客户端请求并转发给内部服务的代理服务。客户端只知道反向代理的存在,不知道真实服务器地址。 |
| 正向代理 | Forward Proxy | 部署在客户端侧,代替客户端访问外部资源的代理服务。服务端看到的是代理的IP,不知道真实客户端。典型应用:VPN、翻墙工具。 |
| API网关 | API Gateway | 位于客户端和后端服务之间的中间层,提供路由、认证、限流、日志等功能,是微服务架构的"统一大门"。 |
| 负载均衡 | Load Balancing | 将请求流量分配到多台服务器,避免单台服务器过载,提高系统可用性和性能。 |
| SSL终结 | SSL Termination | 在网关层处理HTTPS加密解密,后端服务使用HTTP,降低后端计算开销,简化证书管理。 |
| 限流 | Rate Limiting | 限制单位时间内的请求数量,防止系统被突发流量压垮。常用算法:令牌桶、漏桶、滑动窗口。 |
| 熔断 | Circuit Breaking | 当依赖服务出现故障时,自动切断调用,防止故障扩散,并提供降级方案。 |
| 会话保持 | Session Persistence | 确保同一客户端的请求始终路由到同一台后端服务器,用于需要保持会话状态的场景。 |
| 健康检查 | Health Check | 定期检查后端服务的健康状态,自动剔除故障节点,保证流量只发送到健康的服务实例。 |
| 灰度发布 | Canary Release | 将少量流量导到新版本,验证稳定性后逐步扩大比例,降低发布风险。 |
| WAF | Web Application Firewall | Web应用防火墙,防护SQL注入、XSS、CC攻击等Web安全威胁。 |
| CDN | Content Delivery Network | 内容分发网络,在全球部署边缘节点,加速静态资源访问。 |