1. 程式人生 > 實用技巧 >漏桶演算法和令牌桶演算法的區別

漏桶演算法和令牌桶演算法的區別

漏桶演算法與令牌桶演算法在表面看起來類似,很容易將兩者混淆。但事實上,這兩者具有截然不同的特性,且為不同的目的而使用。漏桶演算法與令牌桶演算法的區別在於:漏桶演算法能夠強行限制資料的傳輸速率。令牌桶演算法能夠在限制資料的平均傳輸速率的同時還允許某種程度的突發傳輸。需要說明的是:在某些情況下,漏桶演算法不能夠有效地使用網路資源。因為漏桶的漏出速率是固定的,所以即使網路中沒有發生擁塞,漏桶演算法也不能使某一個單獨的資料流達到埠速率。因此,漏桶演算法對於存在突發特性的流量來說缺乏效率。而令牌桶演算法則能夠滿足這些具有突發特性的流量。通常,漏桶演算法與令牌桶演算法結合起來為網路流量提供更高效的控制

1|0漏桶限流演算法的原理

以固定速率從桶中流出水滴,以任意速率往桶中放入水滴,桶容量大小是不會發生改變的。

流入:以任意速率往桶中放入水滴。

流出:以固定速率從桶中流出水滴。

水滴:是唯一不重複的標識。

因為桶中的容量是固定的,如果流入水滴的速率>流出的水滴速率,桶中的水滴可能會溢位。那麼溢位的水滴請求都是拒絕訪問的,或者直接呼叫服務降級方法。前提是同一時刻

2|0令牌桶演算法(Token)

令牌桶分為2個動作,動作1(固定速率往桶中存入令牌)、動作2(客戶端如果想訪問請求,先從桶中獲取token)。guava 提供的RateLimiter類來進行限流處理。

1.傳統的方式整合RateLimiter 有很大的缺點:程式碼重複量特別大,而且本身不支援註解方式。 2.如果限流程式碼可以放在閘道器中,相當於針對所有的服務介面都實現限流(可以使用排除法進行排除不進行限流的方法),維護性不是很強。 3.正常的網際網路公司專案,不是所有的服務介面都需要實現限流方法的,一般只真針對於大流量介面。比如:秒殺搶購、12306搶票等。 4.可以手動封裝一個RateLimiter類 註解來解決這個方法


以規定的速率往令牌桶中放入 token,使用者請求必須獲取到令牌桶中的 token才可以訪問我們的業務邏輯方法,如果沒有從令牌桶中獲取到 token ,拒絕訪問。
在高併發情況下,如果我們的請求過多 超出了令牌桶生成令牌的速度,這時候請求就會被駁回,提示請稍後重試!
優勢:能夠控制請求的速率。

限流的目的:為了保護服務,避免服務宕機。

3|0使用RateLimiter實現令牌桶限流

1,RateLimiter是guava提供的基於令牌桶演算法的實現類,可以非常簡單的完成限流特技,並且根據系統的實際情況來調整生成token的速率。
通常可應用於搶購限流防止沖垮系統;限制某介面、服務單位時間內的訪問量,譬如一些第三方服務會對使用者訪問量進行限制;限制網速,單位時間內只允許上傳下載多少位元組等。

guava的maven依賴

<dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>25.1-jre</version>
 </dependency>

2,令牌桶的原理,有一個獨立執行緒一直以一個固定的速率往桶中存放令牌

客戶端去桶中獲取令牌,獲取到令牌,就可以訪問,獲取不到,說明請求過多,需要服務降級。

3,

package com.aiyuesheng.controller;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.aiyuesheng.hystrix.OrderHystrixCommand;
import com.aiyuesheng.service.OrderService;
import com.aiyuesheng.utils.LimitService;
import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.RateLimiter;

@RestController
public class Index {// 令牌桶:1.0 表示 每秒中生成1個令牌存放在桶中
    RateLimiter rateLimiter = RateLimiter.create(1.0);

    @Autowired
    private OrderService orderService;
//令牌桶限流
    @RequestMapping("/searchCustomerInfoByRateLimiter")
    public Object searchCustomerInfoByRateLimiter() {
        // 1.限流判斷
        // 如果在0.5秒內 沒有獲取不到令牌的話,則會一直等待
        System.out.println("生成令牌等待時間:" + rateLimiter.acquire());
        boolean acquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS); // 每次傳送請求,願意等待0.5秒,如果設為1秒,每次都能查詢成功,因為沒秒中都會放入一個令牌到桶中
        if (!acquire) {
            System.out.println("稍後再試!");
            return "稍後再試!";
        }
        // 2.如果沒有達到限流的要求,直接呼叫介面查詢
        System.out.println(orderService.searchCustomerInfo());
        return orderService.searchCustomerInfo();
    }

}

4|0總結

常用的限流演算法有兩種:漏桶演算法和令牌桶演算法。

漏桶演算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水,當水流入速度過大會直接溢位,可以看出漏桶演算法能強行限制資料的傳輸速率

對於很多應用場景來說,除了要求能夠限制資料的平均傳輸速率外,還要求允許某種程度的突發傳輸。這時候漏桶演算法可能就不合適了,令牌桶演算法更為適合。如圖2所示,令牌桶演算法的原理是系統會以一個恆定的速度往桶裡放入令牌,而如果請求需要被處理,則需要先從桶裡獲取一個令牌,當桶裡沒有令牌可取時,則拒絕服務

並不能說明令牌桶一定比漏洞好,她們使用場景不一樣。令牌桶可以用來保護自己,主要用來對呼叫者頻率進行限流,為的是讓自己不被打垮。所以如果自己本身有處理能力的時候,如果流量突發(實際消費能力強於配置的流量限制),那麼實際處理速率可以超過配置的限制。而漏桶演算法,這是用來保護他人,也就是保護他所呼叫的系統。主要場景是,當呼叫的第三方系統本身沒有保護機制,或者有流量限制的時候,我們的呼叫速度不能超過他的限制,由於我們不能更改第三方系統,所以只有在主調方控制。這個時候,即使流量突發,也必須捨棄。因為消費能力是第三方決定的。

總結起來:如果要讓自己的系統不被打垮,用令牌桶。如果保證被別人的系統不被打垮,用漏桶演算法。