限流
限流是保护服务不被过载请求打垮的重要手段,通过限制请求速率,确保服务在容量范围内稳定运行。限流可以防止恶意攻击、流量突增、资源耗尽。
为什么需要限流
保护服务:服务容量有限,超过容量的请求会导致服务响应变慢或崩溃。
保护依赖:服务依赖的第三方服务有容量限制(如 API 配额),超过限制会被封禁。
防止雪崩:服务故障时,限流可以减少故障服务的请求,避免雪崩。
公平使用:防止个别用户占用过多资源,保证其他用户的正常使用。
限流算法
固定窗口
固定窗口将时间分成固定大小的窗口,如 1 分钟,每个窗口内限制请求数。请求到达时,检查当前窗口的请求数,如果未超过限制则放行,否则拒绝。
固定窗口的问题:窗口边界问题(窗口边界处可能有双倍请求)、不平滑(窗口前期大量请求,后期无请求)。
滑动窗口
滑动窗口将时间分成更小的单元,如 1 秒,记录每个单元的请求数。请求到达时,统计最近时间窗口(如 1 分钟)的请求数,如果未超过限制则放行,否则拒绝。
滑动窗口的优势:平滑、无边界问题。
滑动窗口的问题:内存占用大(需要记录每个单元的请求数)、实现复杂。
漏桶
漏桶以恒定速率处理请求,请求先进入桶中,桶满后拒绝请求。漏桶的速率固定,输出平滑。
漏桶的优势:平滑输出、可以应对突发流量(桶可以缓存一定请求)。
漏桶的问题:无法应对持续突发流量(桶满后拒绝请求)、桶的大小需要合理设置。
令牌桶
令牌桶以恒定速率放入令牌,请求到达时消耗令牌,有令牌则放行,否则拒绝。令牌桶可以应对突发流量(桶中可以积累令牌)。
令牌桶的优势:可以应对突发流量、限流速率可变(动态调整令牌放入速率)。
令牌桶的问题:实现复杂、需要合理设置桶的大小和令牌放入速率。
分布式限流
单机限流只能限制单个实例的请求,分布式限流需要限制所有实例的总请求。分布式限流可以通过 Redis 实现,使用 INCR 或 Lua 脚本实现原子计数。
分布式限流的问题:Redis 成为瓶颈、Redis 故障影响限流、网络延迟影响限流精度。
限流维度
QPS 限流
每秒请求数限流,限制每秒的请求数。适用于短时间请求。
并发限流
并发请求数限流,限制同时处理的请求数。适用于长连接请求。
用户限流
根据用户 ID 限流,限制每个用户的请求速率。防止个别用户占用过多资源。
IP 限流
根据 IP 限流,限制每个 IP 的请求速率。防止恶意攻击。
接口限流
根据接口限流,不同接口设置不同的限流策略。核心接口限流宽松,非核心接口限流严格。
限流策略
直接拒绝
请求超过限流阈值时直接拒绝,返回错误码或错误信息。简单但影响用户体验。
排队等待
请求超过限流阈值时排队等待,有请求完成则处理排队请求。可以保证所有请求最终被处理,但会增加延迟。
Warm Up
限流阈值从较小值逐渐增加到目标值,避免系统启动时大量请求导致故障。适用于缓存预热、系统启动。
匀速排队
请求以匀速通过,平滑处理请求。漏桶和令牌桶都可以实现匀速排队。
主流限流实现
Sentinel
Sentinel 是阿里开源的流量防卫组件,提供限流、熔断降级、系统保护。
Sentinel 的限流:QPS 限流(直接拒绝、Warm Up、匀速排队)、并发线程数限流、热点参数限流。
Sentinel 的优势:性能高、实时监控、规则动态配置、生态完善。
Guava RateLimiter
Guava RateLimiter 是 Google Guava 的限流库,基于令牌桶算法。
Guava RateLimiter 的特性:平滑限流、支持预热、简单易用。
Guava RateLimiter 的问题:单机限流、不支持分布式。
Redis + Lua
使用 Redis 存储计数,Lua 脚本保证原子性,可以实现分布式限流。
Redis + Lua 的优势:分布式、性能高、灵活。
Redis + Lua 的问题:Redis 成为瓶颈、需要处理 Redis 故障。
Nginx 限流
Nginx 提供 limit_req_module(漏桶)和 limit_conn_module(并发限流)。
Nginx 限流的优势:网关层限流、性能高、配置简单。
Nginx 限流的问题:只能限制经过 Nginx 的请求、不支持复杂限流策略。
限流的最佳实践
分层限流:网关层限流(粗粒度)、应用层限流(细粒度),形成多级防护。
限流阈值设置:根据压测结果设置限流阈值,留有余量。根据业务重要性设置不同的限流策略。
限流监控:监控限流触发情况,分析限流原因,调整限流策略。
限流告警:限流触发时及时告警,便于快速响应。
限流降级:限流触发时可以降级处理,返回默认值或错误响应。
限流预热:系统启动时使用 Warm Up 策略,避免启动时大量请求导致故障。
限流是保护服务的重要手段,理解限流算法和权衡,有助于设计合适的限流方案。