「微服務(wù)設(shè)計(jì)之禪」超時(shí)模式
前言
微服務(wù)本質(zhì)上分布式架構(gòu),當(dāng)我們使用分布式系統(tǒng)時(shí)任何不可預(yù)知的問題都會(huì)發(fā)生(例如網(wǎng)絡(luò)可用性問題、服務(wù)可用性問題、中間件可用性問題)。一個(gè)系統(tǒng)的問題可能會(huì)直接影響另外一個(gè)系統(tǒng)的使用或性能。所以在系統(tǒng)設(shè)計(jì)過程既要保證自身運(yùn)行的彈性需求,也要避免對(duì)下游服務(wù)級(jí)聯(lián)故障。
超時(shí)模式
如下圖在微服務(wù)架構(gòu)中,當(dāng)存在多個(gè)服務(wù)(A,B,C,D),服務(wù) A 依賴于服務(wù) B,而服務(wù) B 依賴于服務(wù) C,依次類推。由于網(wǎng)絡(luò)可用性問題,導(dǎo)致終點(diǎn)服務(wù)(服務(wù) D)不能即使返回,最終導(dǎo)致服務(wù) A 作為調(diào)用方 線程一直處于阻塞狀態(tài)。

所以為了避免上圖所述的線程阻塞的問題,我們建議在依賴服務(wù)之間通過設(shè)置調(diào)用超時(shí)來避免服務(wù)緩慢或者不可用的問題。
| 超時(shí)設(shè)置的好處 |
|---|
| 即使被調(diào)用方服務(wù)不可用,也能保證消費(fèi)方服務(wù)始終正在運(yùn)行 |
| 避免消費(fèi)方服務(wù)無限期等待 |
| 避免阻塞當(dāng)前線程 |
示例程序
架構(gòu)說明

如上圖所示,簡(jiǎn)單模擬電商支付下單邏輯
用戶登錄瀏覽商品 (商品庫存模塊) 扣減商品庫存 (商品庫存模塊) 創(chuàng)建商品訂單 (訂單模塊) 調(diào)用支付模塊支付 (支付模塊)
代碼實(shí)現(xiàn)
├──?timeout-demo
???├──?order-service????????#訂單服務(wù)??(8070)
???├──?pay-service??????????#支付服務(wù)??(8060)
???└──?product-service??????#商品庫存服務(wù)??(8050)
依賴說明。由于 hystrix 年久失修,這里使用 resilience4j 斷路保護(hù)器做演示
<dependency>
????<groupId>io.github.resilience4jgroupId>
????<artifactId>resilience4j-spring-boot2artifactId>
????<version>1.6.1version>
dependency>
消費(fèi)方定義超時(shí)策略
#?超時(shí)參數(shù)配置
resilience4j:
??timelimiter:
????instances:
??????createOrder:?#?接口名稱
????????timeoutDuration:?5s?#超時(shí)時(shí)間
??????pay:?#?接口名稱
????????timeoutDuration:?3s?#超時(shí)時(shí)間
消費(fèi)方接口。product-service 8050
@GetMapping("/buy")
public?String?buy()?{
??log.info("-->?開始調(diào)用?");
??//?模擬調(diào)用?訂單服務(wù)下單
??orderService.createOrder()
??????????//?模擬調(diào)用?支付服務(wù)支付
??????????.thenApply(orderNo?->?payService.pay()).get()
??????????.get();
??log.info("-->?結(jié)束調(diào)用?");
??return?"success";
}
/**
?*?創(chuàng)建訂單
?*?name:?指定接口超時(shí)配置名稱
?*?fallbackMethod:?超時(shí)后降級(jí)方法
?*/
@TimeLimiter(name?=?"createOrder",?fallbackMethod?=?"getError")
public?CompletableFuture?createOrder()? {
????return?CompletableFuture.supplyAsync(()?->?restTemplate.getForEntity("http://localhost:8070/createOrder"
????????????,?String.class).getBody());
}
/**
?*?支付
?*/
@TimeLimiter(name?=?"pay",?fallbackMethod?=?"getError")
public?CompletableFuture?pay()? {
????return?CompletableFuture.supplyAsync(()?->?restTemplate.getForEntity("http://localhost:8060/pay"
????????????,?String.class).getBody());
}
/**
?*?超時(shí)后執(zhí)行降級(jí)方法
?*/
public?CompletableFuture?getError(Throwable?error)? {
????log.warn("失敗?{}",?error.getMessage());
????return?CompletableFuture.completedFuture("");
}
服務(wù)提供方。order-service 8070 /pay-service 8060
@RestController
public?class?PayController?{
????@SneakyThrows
????@GetMapping("/pay")
????public?String?pay(){
????????//?模擬調(diào)用支付渠道耗時(shí)?10s
????????Thread.sleep(10000);
????????return?"支付成功";
????}
}
@RestController
public?class?OrderController?{
????@SneakyThrows
????@GetMapping("/createOrder")
????public?String?createOrder(){
????????//?模擬創(chuàng)建訂單耗時(shí)
????????Thread.sleep(10000);
????????return?"創(chuàng)建訂單服務(wù)";
????}
}
使用示例
通過以上代碼,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的下單邏輯,通過調(diào)用商品服務(wù)接口,會(huì)自動(dòng)調(diào)用支付、訂單服務(wù)接口。訂單、支付服務(wù)提供方 處理的耗時(shí)分別為 10S ,但服務(wù)消費(fèi)方(商品服務(wù)) 針對(duì) createOrder、pay 接口的最大超時(shí)時(shí)間為 5S、3S。所以當(dāng)用戶調(diào)用商品服務(wù)的 BUY 會(huì)在 8S 內(nèi)獲得返回。
測(cè)試如下
curl?http://localhost:8050/buy
日志輸出如下:8 秒超時(shí)按照降級(jí)方法返回。
2020-12-05?14:09:34.605??ProductController???????:?-->?開始調(diào)用
2020-12-05?14:09:39.626??OrderService???:?創(chuàng)建訂單失敗了?TimeLimiter?'createOrder'?recorded?a?timeout?exception.
2020-12-05?14:09:42.644??PayService???:?支付訂單失敗?TimeLimiter?'pay'?recorded?a?timeout?exception.
2020-12-05?14:09:42.645ProductController???????:?-->?結(jié)束調(diào)用
總結(jié)
通過引入 resilience4j 包裝接口,實(shí)現(xiàn)對(duì)指定調(diào)用超時(shí)設(shè)置,保證線程不會(huì)被阻塞
核心服務(wù)不會(huì)因?yàn)橄掠畏?wù)超時(shí)而被影響造成性能問題
保證應(yīng)用返回時(shí)間在固定時(shí)間窗內(nèi)

問題分析:
下游服務(wù)不可用的狀態(tài)下,線程仍需要阻塞,當(dāng)并發(fā)請(qǐng)求很多時(shí),也會(huì)造成性能瓶頸

如上問題可以通過隔離模式來實(shí)現(xiàn)影響最小化,下篇再來講解。
源碼:https://github.com/lltx/microservices-pattern[1]
參考資料和部分圖片來源 https://www.vinsguru.com[2]
參考資料
源碼:: https://github.com/lltx/microservices-pattern
[2]參考資料和部分圖片來源: https://www.vinsguru.com
往期推薦
