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,這就又回到了咱們開始討論的狀況,也就是變成了一個點。

相關文章
相關標籤/搜索