限流、漏斗桶和令牌桶的区别

众所周知:限流是保护系统不被流量冲垮的核心手段,老朋友返场,咱们再来重温一下

一、 核心机制解析

1. 漏桶算法(Leaky Bucket):强制匀速,绝对平滑
  • 工作原理 :把请求想象成"水",漏桶是一个有固定容量的容器。水(请求)以任意速率流入桶中,但桶底有一个固定大小的孔,水只能以恒定的速率流出(被处理)。如果流入的水太多,桶满了之后,多余的水就会溢出(被直接丢弃或拒绝)。
  • 核心特性:它强行抹平了突发流量,保证了下游系统接收到的请求速率是绝对平滑、恒定的。
2. 令牌桶算法(Token Bucket):允许突发,兼顾平滑
  • 工作原理:系统以恒定的速率向一个容量上限固定的"桶"中生成并放入令牌。请求在处理前,必须先消耗对应数量的令牌。如果桶里的令牌足够,请求就能被处理;如果令牌不足,请求就会被限流(丢弃、排队或标记降级)。当桶满时,新生成的令牌会溢出丢弃。
  • 核心特性 :在维持长期平均速率的同时,允许一定程度的突发流量(只要桶里有积攒的令牌,瞬间的大量请求也能被放行)。
考量维度 令牌桶 (Token Bucket) 漏桶 (Leaky Bucket)
突发流量处理 支持良好:允许短时流量超限(消耗库存令牌) 严格限制:输出绝对平滑,无视突发请求
流量平滑度 相对平滑,允许一定波动 完全平滑,无任何波动
延迟与性能 延迟较低,处理效率高 可能会增加排队延迟(水在桶中等待流出)
核心价值 兼顾平均速率与瞬时突发处理能力 提供极强的确定性,保护下游不被打垮

二、 架构选型:根据业务场景匹配

在实际的系统架构设计中,选择哪种算法并非技术优劣的比拼,而是业务场景的匹配度问题:

1. 什么时候选"令牌桶"?
  • 适用场景:API 网关、Web 服务、常规接口限流。
  • 业务诉求:系统本身具备一定的弹性处理能力,希望尽可能多地承接用户的瞬时请求(如秒杀、抢购、突发热点事件),而不是生硬地拒绝用户。
  • 实战落地 :在分布式环境中,常通过 Redis + Lua 脚本实现原子性的分布式令牌桶;在单机应用中,Java 生态常使用 Guava 的 RateLimiter 结合协程来优化高并发性能。
1、 应用层限流:Guava RateLimiter 实战

保护本地核心资源(如数据库写入、第三方API调用),使用令牌桶算法 。Guava 的 RateLimiter经测试绝对是单机限流的绝对王者。

2、 分布式限流:Redis + Lua 脚本实战

场景 :微服务集群下,多个实例共享限流状态。必须使用 Lua 脚本 保证"查询+修改"的原子性,防止并发下的计数错误。

复制代码
-- limit.lua (Redis 脚本)
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])

local current = redis.call('INCR', key)
if current == 1 then
    redis.call('EXPIRE', key, window)
end

if current > limit then
    return 0
end
return 1


// Java 调用封装
public boolean isAllowed(String key, int limit, int windowSec) {
    DefaultRedisScript<Long> script = new DefaultRedisScript<>();
    script.setScriptText(LUA_SCRIPT); // 注入上面的 lua 脚本
    script.setResultType(Long.class);

    Long result = redisTemplate.execute(script, 
        Collections.singletonList(key), 
        String.valueOf(limit), 
        String.valueOf(windowSec));
        
    return result != null && result == 1L;
}
2. 什么时候选"漏桶"?
  • 适用场景:消息队列消费端、支付网关、核心数据库写入接口。
  • 业务诉求:下游系统的处理能力极其有限且固定(例如第三方银行接口限制每秒只能处理 50 笔支付)。此时不需要突发,只需要保证绝对不超频,漏桶的"确定性"是救命稻草。
1、网关层限流:Spring Cloud Gateway 落地
复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100  # 令牌桶每秒填充速率 (QPS)
                redis-rate-limiter.burstCapacity: 200  # 令牌桶最大容量 (允许突发)
                key-resolver: "#{@ipKeyResolver}"     # 限流维度:按IP限流

@Configuration
public class RateLimiterConfig {
    // 根据请求者 IP 进行限流
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
}

三、 实站:混合策略与多层级治理

聪明的你也能猜到:在成熟的架构中,往往不会单一使用某种算法,而是采用混合策略与多层级治理

  1. 网关层(令牌桶):作为系统的第一道防线,使用令牌桶应对前端用户的突发流量,在入口处拦截恶意请求,节约后端资源。如使用 Spring Cloud Gateway + Redis 令牌桶
  2. 应用层(差异化限流):根据业务重要性实施细粒度控制。例如,对于"创建订单"这种核心且耗时的接口,设置较低的 QPS 限制;对于"查询订单"这种轻量级接口,设置较高的 QPS 限制。如使用 Guava RateLimiter
  3. 核心服务层(漏桶):在调用外部依赖或写入核心数据库时,使用漏桶进行兜底防护,确保下游组件的绝对安全与稳定。

总结 :如果你需要保护下游不被冲垮,用漏桶 ;如果你想在保证系统稳定的前提下,尽可能多地承接用户的瞬时热情,用令牌桶

相关推荐
无限的鲜花1 小时前
反射(原创推荐)
java·开发语言
IT二叔1 小时前
Java项目部署-03-teamcity-cicd-docker镜像流水线方式部署
java·ci/cd·持续部署
yongche_shi1 小时前
ragas官方文档中文版(五十)
开发语言·python·ai·ragas·如何评估和改进 rag 应用
一路向北he2 小时前
字节钢铁军团--“提供情境,而非控制”
java·开发语言·前端
超级数据查看器2 小时前
超级数据查看器 v10.0 发布
java·大数据·数据库·sqlite·安卓
Waay2 小时前
面试口述版:个人对 Prometheus 完整理解
运维·学习·云原生·面试·职场和发展·kubernetes·prometheus
AI行业学习3 小时前
Notepad++ 官方下载 + 完整安装 + 全套优化配置(2026最新)
开发语言·人工智能·python·前端框架·html·notepad++
折哥的程序人生 · 物流技术专研3 小时前
《Java 100 天进阶之路》第50篇:阻塞队列与并发容器(2026版)
java·面试题·java进阶·blockingqueue·并发容器·集合源码·java100天进阶
ai_coder_ai3 小时前
编写自动化脚本,在自己后端服务中使用Open Api进行设备相关操作
java·运维·自动化
大圣编程4 小时前
Python中continue语句的用法是什么?
开发语言·前端·python