博客
关于我
RateLimiter源码解析
阅读量:414 次
发布时间:2019-03-06

本文共 5343 字,大约阅读时间需要 17 分钟。

Guava库中的RateLimiter是一个强大的限流器,采用令牌桶算法来控制消息消费速度,防止下游服务因为消息过载而崩溃。RateLimiter的核心实现分为两类:基础限流器SmoothBursty和带有预热功能的SmoothWarmingUp。以下从源码角度详细解析其工作原理。

RateLimiter的基本结构

RateLimiter作为顶层类,主要通过静态工厂方法创建实例。它内部维护两个关键字段:

  • stopwatch:用于计算时间间隔,实现令牌的获取与等待。
  • mutexDoNotUseDirectly:用于线程同步,防止并发访问带来的竞态条件。

创建实例的工厂方法

RateLimiter提供了三种工厂方法:

  • create(double permitsPerSecond):创建基础限流器SmoothBursty。
  • create(double permitsPerSecond, Duration warmupPeriod):创建带预热功能的限流器SmoothWarmingUp。
  • create(double permitsPerSecond, long warmupPeriod, TimeUnit unit):同上,提供统一的接口。
  • 其中,permitsPerSecond表示每秒产生的令牌数量,warmupPeriod是预热阶段的时间,用于从慢速到快速平滑过渡。

    SmoothBursty: 基础限流器

    SmoothBursty是RateLimiter的基础实现,主要用于平稳化消息的生产速率。其内部维护了以下字段:

    • storedPermits:当前桶中的令牌数量。
    • maxPermits:桶的最大容量,通常等于permitsPerSecond。
    • stableIntervalMicros:稳定速率下生产一个令牌所需的时间,单位为微秒。

    构造过程

    SmoothBursty的构造过程如下:

    public static RateLimiter create(double permitsPerSecond) {    return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer());}static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) {    RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);    rateLimiter.setRate(permitsPerSecond);    return rateLimiter;}

    构造时,传入stopwatch和maxBurstSeconds(默认为1秒),maxBurstSeconds限制了桶中令牌的最大持有时间。

    setRate方法

    setRate用于调整限流器的速率,实现如下:

    public final void setRate(double permitsPerSecond) {    checkArgument(permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");    synchronized (mutex()) {        doSetRate(permitsPerSecond, stopwatch.readMicros());    }}

    setRate首先校验参数,确保permitsPerSecond为正值。然后在同步块中执行doSetRate,调整稳定速率下的令牌生产间隔。

    令牌获取逻辑

    SmoothBursty的acquire方法用于获取令牌:

    public double acquire() {    return acquire(1);}public double acquire(int permits) {    long microsToWait = reserve(permits);    stopwatch.sleepMicrosUninterruptibly(microsToWait);    return 1.0 * microsToWait / SECONDS.toMicros(1L);}

    acquire方法通过reserve获取所需的令牌数量,若桶中有足够的令牌,直接返回;否则,线程阻塞,直到获取到令牌。

    reserve方法的核心逻辑:

    final long reserve(int permits) {    checkPermits(permits);    synchronized (mutex()) {        return reserveAndGetWaitLength(permits, stopwatch.readMicros());    }}final long reserveAndGetWaitLength(int permits, long nowMicros) {    long momentAvailable = reserveEarliestAvailable(permits, nowMicros);    return max(momentAvailable - nowMicros, 0);}final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {    resync(nowMicros);    long returnValue = nextFreeTicketMicros;    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);    double freshPermits = requiredPermits - storedPermitsToSpend;    long waitMicros =         storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)         + (long) (freshPermits * stableIntervalMicros);    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);    this.storedPermits -= storedPermitsToSpend;    return returnValue;}

    resync方法用于同步当前时间,更新桶中的令牌数量和下次获取时间。reserveEarliestAvailable方法根据当前时间决定是否需要等待,通过计算桶中令牌的存储情况,决定下一次获取令牌的时间。

    SmoothWarmingUp: 带预热功能的限流器

    SmoothWarmingUp在SmoothBursty的基础上增加了预热功能,以平滑地从低速率向高速率过渡。其内部维护了额外的字段:

    • warmupPeriodMicros:预热阶段的总时间。
    • coldFactor:冷启动时的速率因子(默认为3.0)。

    构造过程

    SmoothWarmingUp的构造过程如下:

    static RateLimiter create(    double permitsPerSecond,     long warmupPeriod,     TimeUnit unit,     double coldFactor,    SleepingStopwatch stopwatch) {    RateLimiter rateLimiter = new SmoothWarmingUp(stopwatch, warmupPeriod, unit, coldFactor);    rateLimiter.setRate(permitsPerSecond);    return rateLimiter;}

    构造时,传入预热时间、时间单位、冷启动因子和stopwatch。

    setRate方法

    setRate方法与SmoothBursty类似,调整限流器的速率:

    public final void setRate(double permitsPerSecond) {    checkArgument(permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");    synchronized (mutex()) {        doSetRate(permitsPerSecond, stopwatch.readMicros());    }}

    doSetRate方法初始化稳定速率下的令牌生产间隔,并更新预热相关参数。

    预热逻辑

    SmoothWarmingUp在resync方法中处理预热逻辑:

    void resync(long nowMicros) {    if (nowMicros > nextFreeTicketMicros) {        double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();        storedPermits = min(maxPermits, storedPermits + newPermits);        nextFreeTicketMicros = nowMicros;    }}

    coolDownIntervalMicros方法返回预热阶段的冷却间隔:

    double coolDownIntervalMicros() {    return warmupPeriodMicros / maxPermits;}

    与SmoothBursty不同,SmoothWarmingUp的最大容量maxPermits可以大于permitsPerSecond,允许预热阶段的灵活配置。

    令牌获取逻辑

    SmoothWarmingUp的acquire方法与SmoothBursty类似,但reserveEarliestAvailable方法增加了对预热阶段的处理:

    final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {    resync(nowMicros);    long returnValue = nextFreeTicketMicros;    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);    double freshPermits = requiredPermits - storedPermitsToSpend;    long waitMicros =         storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)         + (long) (freshPermits * stableIntervalMicros);    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);    this.storedPermits -= storedPermitsToSpend;    return returnValue;}

    storedPermitsToWaitTime方法根据当前桶中的令牌数量和所需的令牌数量,计算需要等待的时间。该方法考虑了预热阶段的梯形区间和稳定速率区间,确保高频率的消息处理不会超过限流器的容量。

    总结

    RateLimiter通过两种不同的实现策略(SmoothBursty和SmoothWarmingUp)满足不同的应用场景。SmoothBursty适合高频率的消息处理,通过简单的桶模型直接控制消息速率。而SmoothWarmingUp则在低频率下提供额外的预热阶段,平滑地从低速率过渡到高速率,避免因突发高负载而导致系统压力过大。两种实现都通过令牌桶算法,确保消息消费的均匀性和系统的稳定性。

    转载地址:http://xlkkz.baihongyu.com/

    你可能感兴趣的文章
    nginx + etcd 动态负载均衡实践(四)—— 基于confd实现
    查看>>
    Nginx + Spring Boot 实现负载均衡
    查看>>
    Nginx + uWSGI + Flask + Vhost
    查看>>
    Nginx - Header详解
    查看>>
    Nginx Location配置总结
    查看>>
    Nginx upstream性能优化
    查看>>
    Nginx 中解决跨域问题
    查看>>
    Nginx 动静分离与负载均衡的实现
    查看>>
    Nginx 反向代理 MinIO 及 ruoyi-vue-pro 配置 MinIO 详解
    查看>>
    Nginx 反向代理解决跨域问题
    查看>>
    Nginx 反向代理配置去除前缀
    查看>>
    nginx 后端获取真实ip
    查看>>
    Nginx 学习总结(17)—— 8 个免费开源 Nginx 管理系统,轻松管理 Nginx 站点配置
    查看>>
    nginx 常用配置记录
    查看>>
    Nginx 我们必须知道的那些事
    查看>>
    Nginx 的 proxy_pass 使用简介
    查看>>
    Nginx 的配置文件中的 keepalive 介绍
    查看>>
    Nginx 负载均衡与权重配置解析
    查看>>
    Nginx 负载均衡详解
    查看>>
    nginx 配置 单页面应用的解决方案
    查看>>