3. 接口限流
限流:是對某一時間窗口內(nèi)的請求數(shù)進(jìn)行限制摊灭,保持系統(tǒng)的可用性和穩(wěn)定性怕轿,防止因流量暴增而導(dǎo)致的系統(tǒng)運(yùn)行緩慢或宕機(jī)
3.1 接口限流
在面臨高并發(fā)的搶購請求時偷崩,我們?nèi)绻粚涌谶M(jìn)行限流,可能會對后臺系統(tǒng)造成極大的壓力撞羽。大量的請求搶購成功時需要調(diào)用下單的接口阐斜,過多的請求打到數(shù)據(jù)庫會對系統(tǒng)的穩(wěn)定性造成影響。
3.2 如何解決接口限流
常用的限流算法有令牌桶
和和漏桶(漏斗算法)
诀紊,而Google開源項目Guava中的RateLimiter使用的就是令牌桶控制算法谒出。在開發(fā)高并發(fā)系統(tǒng)時有三把利器用來保護(hù)系統(tǒng):緩存
、降級
和限流
- 緩存:緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)處理容量
- 降級:降級是當(dāng)服務(wù)器壓力劇增的情況下邻奠,根據(jù)當(dāng)前業(yè)務(wù)情況及流量對一些服務(wù)和頁面有策略的降級笤喳,以此釋放服務(wù)器資源以保證核心任務(wù)的正常運(yùn)行
- 限流:限流的目的是通過對并發(fā)訪問/請求進(jìn)行限速,或者對一個時間窗口內(nèi)的請求進(jìn)行限速來保護(hù)系統(tǒng)碌宴,一旦達(dá)到限制速率則可以拒絕服務(wù)杀狡、排隊或等待、降級等處理贰镣。
3.3 令牌桶和漏斗算法
漏斗算法:漏桶算法思路很簡單捣卤,水(請求)先進(jìn)入到漏桶里忍抽,漏桶以一定的速度出水,當(dāng)水流入速度過大會直接溢出董朝,可以看出漏桶算法能強(qiáng)行限制數(shù)據(jù)的傳輸速率鸠项。
-
令牌桶算法:
最初來源于計算機(jī)網(wǎng)絡(luò)。在網(wǎng)絡(luò)傳輸數(shù)據(jù)時子姜,為了防止網(wǎng)絡(luò)擁塞祟绊,需限制流出網(wǎng)絡(luò)的流量,使流量以比較均勻的速度向外發(fā)送哥捕。令牌桶算法就實現(xiàn)了這個功能牧抽,可控制發(fā)送到網(wǎng)絡(luò)上數(shù)據(jù)的數(shù)目,并允許突發(fā)數(shù)據(jù)的發(fā)送遥赚。大小固定的令牌桶可自行以恒定的速率源源不斷地產(chǎn)生令牌扬舒。如果令牌不被消耗,或者被消耗的速度小于產(chǎn)生的速度凫佛,令牌就會不斷地增多讲坎,直到把桶填滿。后面再產(chǎn)生的令牌就會從桶中溢出愧薛。最后桶中可以保存的最大令牌數(shù)永遠(yuǎn)不會超過桶的大小晨炕。這意味,面對瞬時大流量毫炉,該算法可以在短時間內(nèi)請求拿到大量令牌瓮栗,而且拿令牌的過程并不是消耗很大的事情。
3.4 令牌桶簡單使用
1. 項目中引入依賴
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
2. 令牌桶算法的基本使用
public class StockController {
@Autowired
private OrderService orderService;
//創(chuàng)建令牌桶實例
private RateLimiter rateLimiter = RateLimiter.create(40);
@GetMapping("sale")
public String sale(Integer id){
//1.沒有獲取到token請求一直知道獲取到token 令牌
//log.info("等待的時間: "+ rateLimiter.acquire());
//2.設(shè)置一個等待時間,如果在等待的時間內(nèi)獲取到了token 令牌,則處理業(yè)務(wù),如果在等待時間內(nèi)沒有獲取到響應(yīng)token則拋棄
if(!rateLimiter.tryAcquire(2, TimeUnit.SECONDS)){
System.out.println("當(dāng)前請求被限流,直接拋棄,無法調(diào)用后續(xù)秒殺邏輯....");
return "搶購失敗!";
}
System.out.println("處理業(yè)務(wù).....................");
return "搶購成功";
}
}
3.5使用令牌桶算法實現(xiàn)樂觀鎖+限流
1. 使用令牌桶改造controller實現(xiàn)樂觀鎖+限流
//開發(fā)一個秒殺方法 樂觀鎖防止超賣+ 令牌桶算法限流
@GetMapping("killtoken")
public String killtoken(Integer id){
System.out.println("秒殺商品的id = " + id);
//加入令牌桶的限流措施
if(!rateLimiter.tryAcquire(3, TimeUnit.SECONDS)){
log.info("拋棄請求: 搶購失敗,當(dāng)前秒殺活動過于火爆,請重試");
return "搶購失敗,當(dāng)前秒殺活動過于火爆,請重試!";
}
try {
//根據(jù)秒殺商品id 去調(diào)用秒殺業(yè)務(wù)
int orderId = orderService.kill(id);
return "秒殺成功,訂單id為: " + String.valueOf(orderId);
}catch (Exception e){
e.printStackTrace();
return e.getMessage();
}
}