介面限流演算法:漏桶演算法和令牌桶演算法
阿新 • • 發佈:2019-05-23
漏桶演算法
漏桶可以看作是一個帶有常量服務時間的單伺服器佇列,如果漏桶(包快取)溢位,那麼資料包會被丟棄。這一點和執行緒池原理是很相似的。
把請求比作是水,水來了都先放進桶裡,並以限定的速度出水,當水來得過猛而出水不夠快時就會導致水直接溢位,即拒絕服務。
需要注意的是,在某些情況下,漏桶演算法不能夠有效地使用網路資源,因為漏桶的漏出速率是固定的,所以即使網路中沒有發生擁塞,漏桶演算法也不能使某一個單獨的資料流達到埠速率。因此,漏桶演算法對於存在突發特性的流量來說缺乏效率。而令牌桶演算法則能夠滿足這些具有突發特性的流量。
令牌桶演算法
令牌桶演算法是網路流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一種演算法。典型情況下,令牌桶演算法用來控制傳送到網路上的資料的數目,並允許突發資料的傳送。
令牌桶演算法的原理是系統會以一個恆定的速度往桶裡放入令牌,而如果請求需要被處理,則需要先從桶裡獲取一個令牌,當桶裡沒有令牌可取時,則拒絕服務。從原理上看,令牌桶演算法和漏桶演算法是相反的,一個“進水”,一個是“漏水”。
單機限流
Google的Guava包中的RateLimiter類就是令牌桶演算法的解決方案。 首先說下單機限流
package yzy.guava.test; import com.google.common.base.Optional; import com.google.common.util.concurrent.RateLimiter; import java.nio.channels.ServerSocketChannel; public class OptionalTest { public void guava() { //guava Optional<Integer> possible = Optional.of(6); if(possible.isPresent()) { System.out.println("possible isPresent:" + possible.isPresent()); System.out.println("possible value:" + possible.get()); ServerSocketChannel s =null; } } public static void main(String[] args) throws InterruptedException { OptionalTest hello = new OptionalTest(); hello.guava(); RateLimiter limiter = RateLimiter.create(1);//限制qps最大為1 System.out.println(limiter.acquire()); //輸出阻塞的時間 Thread.sleep(2000); System.out.println(limiter.acquire() + " " + System.currentTimeMillis() / 1000 ); System.out.println(limiter.acquire() + " " + System.currentTimeMillis() / 1000); System.out.println(limiter.acquire() + " " + System.currentTimeMillis() / 1000); System.out.println(limiter.acquire() + " " + System.currentTimeMillis() / 1000); System.out.println(limiter.acquire() + " " + System.currentTimeMillis() / 1000); System.out.println(limiter.acquire() + " " + System.currentTimeMillis() / 1000); } }
分散式限流
基於Redis的分散式限流器可以用來在分散式環境下現在請求方的呼叫頻率。既適用於不同Redisson例項下的多執行緒限流,也適用於相同Redisson例項下的多執行緒限流。該演算法不保證公平性。
RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter");
// 初始化
// 最大流速 = 每1秒鐘產生10個令牌
rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
// 獲取4個令牌
rateLimiter.tryAcquire(4);
// 嘗試獲取4個令牌,嘗試等待時間為2秒鐘
rateLimiter.tryAcquire(4, 2, TimeUnit.SECONDS);
rateLimiter.tryAcquireAsync(2, 2, TimeUnit.SECONDS);
// 嘗試獲取1個令牌,等待時間不限
rateLimiter.acquire();
// 嘗試獲取1個令牌,等待時間不限
RFuture<Void> future = rateLimiter.acqu