第四章:API网关与流量治理
说实话,网关这东西,我刚入行时觉得它就是个“转发器”。直到有一次线上事故——某个接口被爬虫刷爆,整个集群都挂了。老板问我:“你不是说网关能挡吗?”我哑口无言。从那以后,我才真正开始研究网关到底该干什么。
今天这一章,咱们就把网关的核心功能、限流算法、熔断降级、灰度发布这些硬骨头啃下来。我保证,讲得接地气,但深度不减。
4.1 网关的核心功能
网关不是简单的反向代理。它更像是系统的“前厅经理”——所有请求都得先过它这一关。
我个人习惯把网关功能分成三类:
- 路由与转发:根据请求路径、Header、参数,把请求送到正确的后端服务。比如
/api/user/**转到用户服务,/api/order/**转到订单服务。 - 安全与认证:统一做鉴权、验签、IP黑白名单。我见过很多团队在每个微服务里重复写登录校验,其实网关做一次就够了。
- 流量治理:限流、熔断、降级、灰度。这是本章的重点,也是网关最值钱的能力。
核心观点:网关是流量的“总闸门”,所有治理策略都应该在这里统一配置,而不是散落在各个服务里。
你想想看,如果每个服务自己搞限流,那限流阈值怎么定?服务A限100,服务B限200,但网关入口的流量是叠加的,根本算不清。所以,流量治理必须集中在网关层。
4.2 限流算法:令牌桶 vs 漏桶
限流是网关最基础的能力。但算法选不对,效果天差地别。我踩过这个坑,跟大家聊聊。
4.2.1 漏桶算法
漏桶算法,说白了就是一个固定大小的桶,水(请求)以恒定速率流出。如果桶满了,新来的水就溢出(被拒绝)。
它的特点是:输出速率平滑。不管前端怎么突发,后端看到的流量都是均匀的。
但问题也很明显——无法应对突发流量。比如秒杀场景,瞬间涌入1000个请求,漏桶只能慢慢放,用户体验极差。
我曾经在一个直播弹幕系统里用了漏桶,结果用户发弹幕要等好几秒才显示。后来才明白,弹幕场景需要一定的突发能力,漏桶不合适。
4.2.2 令牌桶算法
令牌桶就灵活多了。它有一个桶,里面放令牌,以固定速率生成。请求来了,先拿令牌,拿到就放行,拿不到就等或拒绝。
关键区别:令牌桶允许一定程度的突发。因为桶里可以积累令牌,最多攒到桶容量。比如桶容量100,生成速率10个/秒,如果空闲了10秒,桶里就有100个令牌。这时候突然来100个请求,可以全部放行。
// 伪代码示例:令牌桶
class TokenBucket {
int capacity; // 桶容量
int tokens; // 当前令牌数
long lastRefill; // 上次补充时间
double rate; // 令牌生成速率(个/秒)
boolean tryAcquire() {
refill(); // 补充令牌
if (tokens > 0) {
tokens--;
return true;
}
return false;
}
void refill() {
long now = System.currentTimeMillis();
double elapsed = (now - lastRefill) / 1000.0;
int newTokens = (int)(elapsed * rate);
tokens = Math.min(capacity, tokens + newTokens);
lastRefill = now;
}
}
我的建议:大部分业务场景用令牌桶就够了。漏桶更适合那些对抖动零容忍的场景,比如数据库写入限流。
4.3 熔断降级
限流是防患于未然,熔断是亡羊补牢。当某个后端服务已经出问题了,网关必须主动切断流量,防止雪崩。
我记得有一次,订单服务因为数据库慢查询,响应时间从10ms飙到10秒。结果所有请求都卡在订单服务上,线程池被打满,网关也跟着挂了。这就是典型的雪崩效应。
熔断器的状态机很简单:
| 状态 | 含义 | 触发条件 |
|---|---|---|
| CLOSED(关闭) | 正常,请求放行 | 初始状态 |
| OPEN(打开) | 熔断,请求直接拒绝 | 错误率超过阈值(如50%) |
| HALF_OPEN(半开) | 试探性放行少量请求 | 熔断一段时间后自动进入 |
降级是熔断后的“兜底”策略。比如订单服务熔断了,网关可以返回一个缓存数据,或者直接提示“系统繁忙,请稍后再试”。说白了,就是有损服务——宁可给用户一个降级响应,也不能让整个系统崩溃。
避坑指南:熔断阈值别设太死。我曾经设了50%错误率熔断,结果某个接口偶尔超时,但整体可用,熔断反而引发了更大范围的不可用。建议结合滑动窗口统计,比如最近10秒内的错误率。
4.4 灰度发布
灰度发布,也叫金丝雀发布。新版本先让一小部分用户用,没问题再全量推。这比蓝绿部署更省钱,也更安全。
网关做灰度,核心就是流量路由策略。常见的策略有:
- 按用户ID哈希:比如 user_id % 100 < 5 的走灰度版本
- 按请求Header:比如 Header 里带
X-Canary: true的走灰度 - 按地域/IP:比如只让北京地区的用户先体验
我个人习惯用权重路由。在网关配置里,给新版本分配5%的权重,老版本95%。观察一段时间,如果新版本无异常,逐步提升权重到100%。
// 灰度路由伪代码
String routeVersion(String userId) {
int hash = userId.hashCode() & 0x7FFFFFFF; // 取正
int bucket = hash % 100;
if (bucket < 5) {
return "v2"; // 灰度版本
} else {
return "v1"; // 稳定版本
}
}
注意:灰度发布一定要有回滚能力。我见过一个团队灰度上线后,新版本有内存泄漏,但因为没配回滚策略,只能紧急重启所有节点。嗯,那场面,挺狼狈的。
4.5 知识体系总览
下面这张图,是我自己总结的网关流量治理知识体系。你可以把它当成一个检查清单——看看你的网关还缺什么。
这张图把网关流量治理拆成了四个维度:限流、熔断降级、灰度发布,以及底层的核心功能。你可以在实际项目中对照着看,哪个维度还没做到位。
最后说一句:网关不是银弹。它解决的是流量入口的问题,但业务逻辑的健壮性还得靠服务自身。别把所有希望都压在网关上,该做的限流、熔断,服务内部也得有。
好了,这一章就到这里。流量治理是个实践性很强的话题,建议你回去看看自己项目的网关配置,说不定能发现不少优化点。