漏桶算法與令牌桶算法在表面看起來類似,很容易將兩者混淆。但事實上,這兩者具有截然不同的特性,且為不同的目的而使用。漏桶算法與令牌桶算法的區別在於:漏桶算法能夠強行限制數據的傳輸速率。令牌桶算法能夠在限制數據的平均傳輸速率的同時還允許某種程度的突發傳輸。需要說明的是:在某些情況下,漏桶算法不能夠有效地使用網絡資源。因為漏桶的漏出速率是固定的,所以即使網絡中沒有發生擁塞,漏桶算法也不能使某一個單獨的數據流達到端口速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率。而令牌桶算法則能夠滿足這些具有突發特性的流量。通常,漏桶算法與令牌桶算法結合起來為網絡流量提供更高效的控制
1|0漏桶限流算法的原理
以固定速率從桶中流出水滴,以任意速率往桶中放入水滴,桶容量大小是不會發生改變的。
流入:以任意速率往桶中放入水滴。
流出:以固定速率從桶中流出水滴。
水滴:是唯一不重復的標識。
因為桶中的容量是固定的,如果流入水滴的速率>流出的水滴速率,桶中的水滴可能會溢出。那么溢出的水滴請求都是拒絕訪問的,或者直接調用服務降級方法。前提是同一時刻
2|0令牌桶算法(Token)
令牌桶分為2個動作,動作1(固定速率往桶中存入令牌)、動作2(客戶端如果想訪問請求,先從桶中獲取token)。guava 提供的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所示,令牌桶算法的原理是系統會以一個恆定的速度往桶里放入令牌,而如果請求需要被處理,則需要先從桶里獲取一個令牌,當桶里沒有令牌可取時,則拒絕服務
並不能說明令牌桶一定比漏洞好,她們使用場景不一樣。令牌桶可以用來保護自己,主要用來對調用者頻率進行限流,為的是讓自己不被打垮。所以如果自己本身有處理能力的時候,如果流量突發(實際消費能力強於配置的流量限制),那么實際處理速率可以超過配置的限制。而漏桶算法,這是用來保護他人,也就是保護他所調用的系統。主要場景是,當調用的第三方系統本身沒有保護機制,或者有流量限制的時候,我們的調用速度不能超過他的限制,由於我們不能更改第三方系統,所以只有在主調方控制。這個時候,即使流量突發,也必須舍棄。因為消費能力是第三方決定的。
總結起來:如果要讓自己的系統不被打垮,用令牌桶。如果保證被別人的系統不被打垮,用漏桶算法。