什么是平滑限流? 平就是平稳、滑是没有折线(好像也不太准确),没有曲线?(曲线其实也能够有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 的预热限流 是一个什么状况?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 应该算是漏桶,通常状况下,也就是当预热时间不为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 的斜率变成0,那么整个加热过程的就是一天直线。可是其实这个在SmoothWarmingUp 中是不可能发生的。 由于SmoothWarmingUp 的一个假设是 预热时间 = 冷却时间。若是斜率变成了0,那么预热时间 = 冷却时间 就不可能知足,除非预热时间变成了0,这就又回到了咱们开始讨论的状况,也就是变成了一个点。