「微服務(wù)設(shè)計(jì)之禪」限流模式
前言
微服務(wù)本質(zhì)上分布式架構(gòu),當(dāng)我們使用分布式系統(tǒng)時(shí)任何不可預(yù)知的問(wèn)題都會(huì)發(fā)生(例如網(wǎng)絡(luò)可用性問(wèn)題、服務(wù)可用性問(wèn)題、中間件可用性問(wèn)題)。一個(gè)系統(tǒng)的問(wèn)題可能會(huì)直接影響另外一個(gè)系統(tǒng)的使用或性能。所以在系統(tǒng)設(shè)計(jì)過(guò)程既要保證自身運(yùn)行的彈性需求,也要避免對(duì)下游服務(wù)級(jí)聯(lián)故障。
限流模式
在微服務(wù)技術(shù)架構(gòu)中,當(dāng)有多個(gè)服務(wù)(A,B,C, D)時(shí),一個(gè)服務(wù)(A)可能依賴于另一服務(wù)(B),而另一服務(wù)(B)又可能依賴于 C,依此類推。
如下圖我們有 2 個(gè)服務(wù) A 和 B,B 服務(wù)要進(jìn)行大量的業(yè)務(wù)計(jì)算和其他依賴接口調(diào)用導(dǎo)致 B 的最大請(qǐng)求處理個(gè)數(shù)小于請(qǐng)求 A 的并發(fā)數(shù)。

當(dāng)服務(wù) A 接收到很多的服務(wù)請(qǐng)求時(shí),由于 B 服務(wù)能夠處理的請(qǐng)求數(shù)量多,從而導(dǎo)致服務(wù)宕機(jī)。
作為服務(wù) B 為了保證自身高,通過(guò)拒絕無(wú)法處理的請(qǐng)求來(lái)保證服務(wù)正常運(yùn)行。

限流模式通過(guò)限制在指定時(shí)間窗內(nèi)運(yùn)行的請(qǐng)求處理數(shù)量,幫助提升服務(wù)的可用性。
示例程序
架構(gòu)圖

如上圖所示,簡(jiǎn)單模擬電商下單邏輯
用戶登錄瀏覽商品 (商品庫(kù)存模塊) 扣減商品庫(kù)存 (商品庫(kù)存模塊) 創(chuàng)建商品訂單 (訂單模塊)
product-service 通過(guò)調(diào)用 order-service 服務(wù)下單
代碼實(shí)現(xiàn)
├──?ratelimiter-demo
???├──?order-service????????#訂單服務(wù)??(8070)
???└──?product-service??????#商品庫(kù)存服務(wù)??(8050)
依賴說(shuō)明。由于 hystrix 年久失修,這里使用 resilience4j 斷路保護(hù)器做演示
<dependency>
????<groupId>io.github.resilience4jgroupId>
????<artifactId>resilience4j-spring-boot2artifactId>
????<version>1.6.1version>
dependency>
針對(duì)接口定義重試策略 10s 內(nèi)最多允許通過(guò) 5 個(gè)請(qǐng)求
resilience4j.ratelimiter:
??instances:
????createOrder:
??????limitForPeriod:?5????????#?最多運(yùn)行多少個(gè)
??????limitRefreshPeriod:?10s??#時(shí)間窗限制?10s
消費(fèi)方接口。product-service 8050
/**
?*?用戶點(diǎn)擊購(gòu)買
?*/
@SneakyThrows
@GetMapping("/order")
public?String?buy()?{
????//?模擬調(diào)用?訂單服務(wù)下單
????orderService.createOrder().get();
????return?"success";
}
定義遠(yuǎn)程調(diào)用類使用 Retry 包裝
/**
?*?創(chuàng)建訂單
?*?name:?指定接口限流配置名稱
?*?fallbackMethod:?限流后降級(jí)方法
?*/
@RateLimiter(name?=?"createOrder",?fallbackMethod?=?"getError")
public?CompletableFuture?createOrder()? {
????return?CompletableFuture.supplyAsync(()?->?restTemplate.getForEntity("http://localhost:8070/createOrder"
????????????,?String.class).getBody());
}
public?CompletableFuture?getError(Throwable?error)? {
????log.warn("創(chuàng)建訂單失敗了?{}",?error.getMessage());
????throw?new?RuntimeException("創(chuàng)建訂單失敗了");
}
服務(wù)提供方。order-service 8070
@RestController
public?class?PayController?{
????@SneakyThrows
????@GetMapping("/createOrder")
????public?String?createOrder()?{
????????return?"創(chuàng)建訂單服務(wù)";
????}
}
開(kāi)始測(cè)試
并發(fā) 10 個(gè)線程,請(qǐng)求商品服務(wù) (由于我們?cè)O(shè)置策略 10 秒內(nèi)只允許 5 個(gè)請(qǐng)求)


訂單服務(wù)日志,由于是第一次請(qǐng)求觸發(fā)異常,然后服務(wù)調(diào)用方自動(dòng)重試產(chǎn)生第二次調(diào)用。
2020-12-07?15:53:46.698??WARN?52265?---?[nio-8050-exec-6]?c.example.product.service.OrderService???:?創(chuàng)建訂單失敗了?RateLimiter?'createOrder'?does?not?permit?further?calls
...?異常日志?...
源碼:https://github.com/lltx/microservices-pattern
參考資料和部分圖片來(lái)源 https://www.vinsguru.com
往期推薦
