1. 程式人生 > >介面限流演算法:漏桶演算法和令牌桶演算法

介面限流演算法:漏桶演算法和令牌桶演算法

漏桶演算法

漏桶可以看作是一個帶有常量服務時間的單伺服器佇列,如果漏桶(包快取)溢位,那麼資料包會被丟棄。這一點和執行緒池原理是很相似的。

把請求比作是水,水來了都先放進桶裡,並以限定的速度出水,當水來得過猛而出水不夠快時就會導致水直接溢位,即拒絕服務。

需要注意的是,在某些情況下,漏桶演算法不能夠有效地使用網路資源,因為漏桶的漏出速率是固定的,所以即使網路中沒有發生擁塞,漏桶演算法也不能使某一個單獨的資料流達到埠速率。因此,漏桶演算法對於存在突發特性的流量來說缺乏效率。而令牌桶演算法則能夠滿足這些具有突發特性的流量。

令牌桶演算法

令牌桶演算法是網路流量整形(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