博客
关于我
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/

    你可能感兴趣的文章
    Nmap渗透测试指南之指纹识别与探测、伺机而动
    查看>>
    Nmap端口扫描工具Windows安装和命令大全(非常详细)零基础入门到精通,收藏这篇就够了
    查看>>
    NMAP网络扫描工具的安装与使用
    查看>>
    NMF(非负矩阵分解)
    查看>>
    nmon_x86_64_centos7工具如何使用
    查看>>
    NN&DL4.1 Deep L-layer neural network简介
    查看>>
    NN&DL4.3 Getting your matrix dimensions right
    查看>>
    NN&DL4.7 Parameters vs Hyperparameters
    查看>>
    NN&DL4.8 What does this have to do with the brain?
    查看>>
    nnU-Net 终极指南
    查看>>
    No 'Access-Control-Allow-Origin' header is present on the requested resource.
    查看>>
    NO 157 去掉禅道访问地址中的zentao
    查看>>
    no available service ‘default‘ found, please make sure registry config corre seata
    查看>>
    No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?
    查看>>
    no connection could be made because the target machine actively refused it.问题解决
    查看>>
    No Datastore Session bound to thread, and configuration does not allow creation of non-transactional
    查看>>
    No fallbackFactory instance of type class com.ruoyi---SpringCloud Alibaba_若依微服务框架改造---工作笔记005
    查看>>
    No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalanc
    查看>>
    No mapping found for HTTP request with URI [/...] in DispatcherServlet with name ...的解决方法
    查看>>
    No mapping found for HTTP request with URI [/logout.do] in DispatcherServlet with name 'springmvc'
    查看>>