设计一套生产级 API 网关,必须同时覆盖路由转发、认证鉴权、限流熔断、协议转换与可观测性五类核心能力。动态路由依赖注册中心与服务发现实现规则热更新;限流在网关层以令牌桶或滑动窗口算法配合 Redis 原子计数落地,才能在集群环境下保持准确。下文给出每项能力的设计要点与可直接复用的实现片段。
一、API 网关必须覆盖的五大核心功能
网关是系统流量的统一入口,缺任何一块都会把风险推到后端服务。五项能力的分工如下:
| 核心功能 | 解决的问题 | 典型实现 |
|---|---|---|
| 路由转发 | 将请求精准分发到后端服务 | 路径 / 域名 / Header 谓词匹配 |
| 认证鉴权 | 统一拦截未授权请求 | JWT、OAuth2.0、API Key |
| 限流熔断 | 防御突发流量与级联雪崩 | 令牌桶、Sentinel 滑动窗口 |
| 协议转换 | 屏蔽内外协议差异 | HTTP ↔ gRPC / Dubbo 适配 |
| 可观测性 | 定位慢调用与异常链路 | 分布式追踪、结构化访问日志 |
阿里云 API 网关采用 Redis + Lua 方案维护全局令牌计数,保证集群限流准确性;Spring Cloud Gateway 通过谓词工厂声明路由规则,二者分别对应”集中式计数”与”声明式配置”两种思路。
二、动态路由的实现路径
动态路由的目标是把写死的转发地址变成可实时变更的规则。落地通常分三步推进:
- 接入注册中心(Nacos / Consul / Eureka),网关订阅服务实例列表变更;
- 在内存维护本地路由缓存,收到变更通知即更新,避免每次请求远程查询;
- 用路由规则引擎按 Path、Header、Query 多维匹配,支持灰度标签(如
version=v2)。
2.1 基于标签的灰度路由
灰度发布时,请求 Header 携带 version 字段,路由引擎将其匹配到对应版本实例,实现 AB 测试而不改业务代码。得物自研网关引入动态路由决策树,预编译条件表达式,将路由耗时从毫秒级压到微秒级,可支撑十万级规则。
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Header=version, v2
三、限流的实现方案
限流保护的是整条后端链路,算法选型直接决定突发容忍度与精度。
3.1 限流算法对比
| 算法 | 突发容忍 | 精度 | 内存开销 | 适用场景 |
|---|---|---|---|---|
| 令牌桶 | 支持 | 中 | 低 | 公开 API、移动端 |
| 漏桶 | 不支持 | 中 | 中 | 流量整形、网络限速 |
| 滑动窗口计数器 | 部分 | 高(约 1% 误差) | 低 | 高并发网关 |
令牌桶因允许可控突发且实现简单,是公开 API 的默认选择。
3.2 分布式限流的原子实现
集群部署下,单机计数器会被网关节点分摊而失效,必须用 Redis + Lua 保证”读取—判断—写入”的原子性。
- 为每个限流维度(如用户 ID + 路径)建立独立限流键;
- 用 Lua 脚本按时间差补充令牌,判断余量后扣减;
- 键设置随容量 / 速率推算的过期时间,空闲自动清理;
- 超限返回 HTTP 429 并带
Retry-After头,引导客户端退避。
-- 令牌桶:Redis + Lua 原子限流
local key = KEYS[1]
local rate = tonumber(ARGV[1]) -- 每秒填充令牌数
local cap = tonumber(ARGV[2]) -- 桶容量(突发上限)
local now = tonumber(ARGV[3])
local data = redis.call("HMGET", key, "tokens", "ts")
local tokens = tonumber(data[1]) or cap
local ts = tonumber(data[2]) or now
tokens = math.min(cap, tokens + (now - ts) / 1000 * rate)
local allowed = 0
if tokens >= 1 then
tokens = tokens - 1
allowed = 1
end
redis.call("HMSET", key, "tokens", tokens, "ts", now)
redis.call("PEXPIRE", key, math.ceil(cap / rate * 1000))
return {allowed, math.floor(tokens)}
Stripe、GitHub 与 AWS API Gateway 均在生产环境采用令牌桶思路。
四、落地时的两个易错点
冷启动击穿是高频问题:系统重启后令牌桶为空,突发流量会瞬间打满后端。用预热模式逐步放开阈值可缓解。限流维度单一同样危险,只按 IP 限流会被 NAT 与代理绕过,应按”用户 / API Key > 用户 ID > IP”的优先级组合维度。
常见问题(FAQ)
Q1:网关和注册中心心跳不一致怎么办?
网关除依赖注册中心外,还应主动健康检查,剔除连续失败的实例。
Q2:令牌桶和漏桶怎么选?
需要容忍短时突发选令牌桶;要求匀速输出、做流量整形选漏桶。
Q3:限流返回值该用什么状态码?
超限统一返回 HTTP 429,并附 Retry-After 与 RateLimit 系列响应头。