RateLimiter 平滑限流之非突发、非预热

平滑限流

什么是平滑限流? 平就是平稳、滑是没有折线(好像也不太准确),没有曲线?(曲线其实也能够有Smooth的意思)丝滑?总之是比较Smooth就对了。guava中RateLimiter 的实现只有平滑限流的实现,即SmoothRateLimiter。 而SmoothRateLimiter 也是抽象的,它有两个实现,一个是突发实现即SmoothBursty,一个是预热实现即SmoothWarmingUp。缓存

突发限流的方法是一个参数,而预热限流的方法是三个参数的方法,具体可见源码:测试

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

可见预热限流是SmoothWarmingUpui

预热时间为0 的预热限流

预热时间为0 的预热限流 是一个什么状况?pwa

注意到上方法的第一个参数是和突发限流的方法是同样的,都是每秒的许可数。上方法的第二个参数是预热时间。最后一个参数是时间单位。 第一个参数天然是不能小于等于0的, 不能失去了意义。 第二个参数固然不能小于0,不然没法解释;那它是否能够等于0? 测试发现是能够的。日志

当预热限流的方法的第二个参数是0的时候,预热的效果就消失,也就是没有了预热。这就变成了一个特殊的状况, 也就是也非突发、既非预热。SmoothWarmingUp的图形变成了一个点! 尽管如此,虽然此时预热时间为0,那么它是否就没有了任何的缓存许可的功能? 非也!token

经过上文的分析,加上测试,发现SmoothWarmingUp 并不会积累permits。 就是说,使用的时候当即达到最高速度,不使用的时候当即冷却,此时速度呢? 其实冷却的时候速度都也不重要,其实是无穷大,可是没有意义,由于加速的过程为0, 因此也能够把冷却的速度理解为0(更好理解)。就是说,基本上SmoothWarmingUp 只会使用2个速度:使用时max,不使用时0;get

RateLimiter r = RateLimiter.create(2, 0, TimeUnit.MILLISECONDS);
漏桶,由于它本质上是令牌桶上作了修改。SmoothWarmingUp源码

SmoothWarmingUp 究竟是漏桶仍是令牌桶

其实上文说错了。答案是明确的,SmoothWarmingUp 应该算是漏桶,通常状况下,也就是当预热时间不为0的时候。咱们知道SmoothWarmingUp有一个冷却的过程,而这个冷却的过程,就是能够理解为漏桶的令牌漏出的过程!可是呢, 正常理解的漏桶,若是桶内令牌足够,应该是能够直接获取而不用等待的(无论是否已经开始了冷却)。而咱们知道SmoothWarmingUp不是的,SmoothWarmingUp一旦开始了冷却,它就必需要至少须要等待一些些的预热时间,也就是说须要比稳定状态时更久的时间!!it

并且,咱们知道,SmoothWarmingUp的整个加热过程(包括预热和准稳定过程) 并非一个直线,而是折线,这和咱们想象中的理想的漏桶恐怕仍是不同。io

测试观察

@org.junit.Test
    public void testSmoothWarmingWith0() {
        RateLimiter r = RateLimiter.create(2, 0, TimeUnit.MILLISECONDS);
        while (true)
        {
            System.out.println("get 2 tokens: " + r.acquire(2) + "s");// 2个须要1s,但许可获取时间由下一次获取承担; 循环的首次,此行等待时间为0,第二次之后的等待时间为⑤ 行的许可获取时间,即0.5s
            try {
                Thread.sleep(1500);// 休息1.5s,能够彻底消耗上行的2个许可,并剩出来0.5s,可是由于 预热/冷却时间为0,因此这个0.5s 实际上是彻底白白的流逝了..
            } catch (Exception e) {
            }
            System.out.println("get 3 tokens: " + r.acquire(3) + "s");// 由于 预热/冷却时间为0,前面的许可已经彻底被补偿,因此此处3个须要1.5s,但时间地点时间为0,许可获取时间由下一次获取承担
            System.out.println("get 1 tokens: " + r.acquire(1) + "s");// 上一个方法的实际等待时间为0,由于它由此方法承担—— 此方法实际等待时间为上一个方法的3个许可获取时间,即1.5s
            System.out.println("get 1 tokens: " + r.acquire(1) + "s");// 此方法实际等待时间为上一个方法的1个许可获取时间,即0.5s
            System.out.println("get 1 tokens: " + r.acquire(1) + "s");// ⑤ 此方法实际等待时间为上一个方法的1个许可获取时间,即0.5s
            System.out.println("end");
        }
    }

观察日志打印,发现符合预期。

get 2 tokens: 0.0s
get 3 tokens: 0.0s
get 1 tokens: 1.499681s
get 1 tokens: 0.494564s
get 1 tokens: 0.498945s
end
get 2 tokens: 0.499013s
get 3 tokens: 0.0s
get 1 tokens: 1.499839s
get 1 tokens: 0.499698s
get 1 tokens: 0.499081s
end
get 2 tokens: 0.499659s
get 3 tokens: 0.0s
get 1 tokens: 1.499842s
get 1 tokens: 0.498895s
get 1 tokens: 0.499479s

SmoothWarmingUp 整个加热过程是否是能够为一条直线?

理论上来说,SmoothWarmingUp 的斜率变成0,那么整个加热过程的就是一天直线。可是其实这个在SmoothWarmingUp 中是不可能发生的。 由于SmoothWarmingUp 的一个假设是 预热时间 = 冷却时间。若是斜率变成了0,那么预热时间 = 冷却时间 就不可能知足,除非预热时间变成了0,这就又回到了咱们开始讨论的状况,也就是变成了一个点。

相关文章
相关标签/搜索