javascript
SpringCloud Alibaba之 Sentinel流量防卫兵
文章目錄
- Sentinel 流量防衛(wèi)兵
- 一、安裝與部署
- 🔆1.1 服務(wù)端 JAR 包
- 🔆1.2 客戶端掛載
- 🔆1.3 Sentinel Dashboard 監(jiān)控
- 二、流量控制
- 🎀2.1 流量控制算法
- 🎀2.2 流控規(guī)則
- 🎀2.3 @SentinelResource 對(duì)方法流控
- 🎀2.4 示例:對(duì)接口流控
- 🎀2.5 系統(tǒng)規(guī)則
- 三、熱點(diǎn)參數(shù)限流
- 四、服務(wù)熔斷和降級(jí)
- 💎4.1 熔斷規(guī)則:慢調(diào)用比例
- 💎4.2 熔斷規(guī)則:異常比例
- 💎4.3 熔斷規(guī)則:異常數(shù)
- 💎4.4 精準(zhǔn)服務(wù)降級(jí)
- 💎4.5 遠(yuǎn)程調(diào)用中的服務(wù)熔斷
- 五、限流 / 熔斷后的異常處理
- 🔑5.1 接口異常處理
- 🔑5.2 方法中 sentinel 異常的處理
- 🔑5.3 方法中其它異常的處理
提示:以下是本篇文章正文內(nèi)容,SpringCloud Alibaba 系列學(xué)習(xí)將會(huì)持續(xù)更新
Spring Cloud Alibaba 官方學(xué)習(xí)文檔
Sentinel 流量防衛(wèi)兵
See the 中文文檔 for document in Chinese.
經(jīng)過(guò)之前的學(xué)習(xí),我們了解了微服務(wù)存在的雪崩問(wèn)題,也就是說(shuō)一個(gè)微服務(wù)出現(xiàn)問(wèn)題,有可能導(dǎo)致整個(gè)鏈路直接不可用,這種時(shí)候我們就需要進(jìn)行及時(shí)的熔斷和降級(jí),這些策略,我們之前通過(guò)使用 Hystrix 來(lái)實(shí)現(xiàn)。
SpringCloud Alibaba 也有自己的微服務(wù)容錯(cuò)組件,但是它相比 Hystrix 更加的強(qiáng)大。
隨著微服務(wù)的流行,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來(lái)越重要。Sentinel 是面向分布式、多語(yǔ)言異構(gòu)化服務(wù)架構(gòu)的流量治理組件,主要以流量為切入點(diǎn),從流量路由、流量控制、流量整形、熔斷降級(jí)、系統(tǒng)自適應(yīng)過(guò)載保護(hù)、熱點(diǎn)流量防護(hù)等多個(gè)維度來(lái)幫助開發(fā)者保障微服務(wù)的穩(wěn)定性。
Sentinel 具有以下特征:
- 豐富的應(yīng)用場(chǎng)景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場(chǎng)景,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)、消息削峰填谷、集群流量控制、實(shí)時(shí)熔斷下游不可用應(yīng)用等。
- 完備的實(shí)時(shí)監(jiān)控:Sentinel 同時(shí)提供實(shí)時(shí)的監(jiān)控功能。您可以在控制臺(tái)中看到接入應(yīng)用的單臺(tái)機(jī)器秒級(jí)數(shù)據(jù),甚至 500 臺(tái)以下規(guī)模的集群的匯總運(yùn)行情況。
- 廣泛的開源生態(tài):Sentinel 提供開箱即用的與其它開源框架/庫(kù)的整合模塊,例如與 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相應(yīng)的依賴并進(jìn)行簡(jiǎn)單的配置即可快速地接入 Sentinel。同時(shí) Sentinel 提供 Java/Go/C++ 等多語(yǔ)言的原生實(shí)現(xiàn)。
- 完善的 SPI 擴(kuò)展機(jī)制:Sentinel 提供簡(jiǎn)單易用、完善的 SPI 擴(kuò)展接口。您可以通過(guò)實(shí)現(xiàn)擴(kuò)展接口來(lái)快速地定制邏輯。例如定制規(guī)則管理、適配動(dòng)態(tài)數(shù)據(jù)源等。
一、安裝與部署
🔆1.1 服務(wù)端 JAR 包
它和 Nacos一樣,它是獨(dú)立安裝和部署的,下載地址:https://github.com/alibaba/Sentinel/releases
注意下載下來(lái)之后是一個(gè) jar 文件(其實(shí)就是個(gè)SpringBoot項(xiàng)目),我們需要在 IDEA 中部署:
啟動(dòng)之后,就可以訪問(wèn)到 Sentinel 的監(jiān)控頁(yè)面了,用戶名和密碼都是 sentinel,地址:http://localhost:8858
🔆1.2 客戶端掛載
這樣就成功開啟監(jiān)控頁(yè)面了,接著我們需要讓我們的服務(wù)連接到 Sentinel 控制臺(tái),老規(guī)矩,導(dǎo)入依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>然后在配置文件中添加 Sentinel 相關(guān)信息(實(shí)際上 Sentinel 是本地在進(jìn)行管理,但是我們可以連接到監(jiān)控頁(yè)面,這樣就可以圖形化操作了):
spring:application:name: userservicecloud:nacos:discovery:server-addr: localhost:8848sentinel:transport:# 添加監(jiān)控頁(yè)面地址即可dashboard: localhost:8858🔆1.3 Sentinel Dashboard 監(jiān)控
現(xiàn)在啟動(dòng)我們的服務(wù),然后訪問(wèn)一次服務(wù),這樣 Sentinel 中就會(huì)存在信息了(懶加載機(jī)制,不會(huì)一上來(lái)就加載):
現(xiàn)在我們就可以在 Sentinel 控制臺(tái)中對(duì)我們的服務(wù)運(yùn)行情況進(jìn)行實(shí)時(shí)監(jiān)控了,可以看到監(jiān)控的內(nèi)容非常的多,包括時(shí)間點(diǎn)、QPS(每秒查詢率)、響應(yīng)時(shí)間等數(shù)據(jù)。
按照上面的方式,我們將所有的服務(wù)全部連接到 Sentinel 管理面板中。
回到目錄…
二、流量控制
前面我們完成了對(duì) Sentinel 的搭建與連接,接著我們來(lái)看看Sentinel的第一個(gè)功能,流量控制。
我們的機(jī)器不可能無(wú)限制的接受和處理客戶端的請(qǐng)求,如果不加以限制,當(dāng)發(fā)生高并發(fā)情況時(shí),系統(tǒng)資源將很快被耗盡。為了避免這種情況,我們就可以添加流量控制(也可以說(shuō)是限流)。當(dāng)一段時(shí)間內(nèi)的流量到達(dá)一定的閾值的時(shí)候,新的請(qǐng)求將不再進(jìn)行處理,這樣不僅可以合理地應(yīng)對(duì)高并發(fā)請(qǐng)求,同時(shí)也能在一定程度上保護(hù)服務(wù)器不受到外界的惡意攻擊。
那么要實(shí)現(xiàn)限流,正常情況下,我們?cè)摬扇∈裁礃拥牟呗阅?#xff1f;
- 方案一:快速拒絕,既然不再接受新的請(qǐng)求,那么我們可以直接返回一個(gè)拒絕信息,告訴用戶訪問(wèn)頻率過(guò)高。
- 方案二:預(yù)熱,依然基于方案一,但是由于某些情況下高并發(fā)請(qǐng)求是在某一時(shí)刻突然到來(lái),我們可以緩慢地將閾值提高到指定閾值,形成一個(gè)緩沖保護(hù)。
- 方案三:排隊(duì)等待,不接受新的請(qǐng)求,但是也不直接拒絕,而是進(jìn)隊(duì)列先等一下,如果規(guī)定時(shí)間內(nèi)能夠執(zhí)行,那么就執(zhí)行,要是超時(shí)就算了。
🎀2.1 流量控制算法
漏桶算法
顧名思義,就像一個(gè)桶開了一個(gè)小孔,水流進(jìn)桶中的速度肯定是遠(yuǎn)大于水流出桶的速度的,這也是最簡(jiǎn)單的一種限流思路:
我們知道,桶是有容量的,所以當(dāng)桶的容量已滿時(shí),就裝不下水了,這時(shí)就只有丟棄請(qǐng)求了。
利用這種思想,我們就可以寫出一個(gè)簡(jiǎn)單的限流算法。
令牌桶算法
只能說(shuō)有點(diǎn)像信號(hào)量機(jī)制。現(xiàn)在有一個(gè)令牌桶,這個(gè)桶是專門存放令牌的,每隔一段時(shí)間就向桶中丟入一個(gè)令牌(速度由我們指定)當(dāng)新的請(qǐng)求到達(dá)時(shí),將從桶中刪除令牌,接著請(qǐng)求就可以通過(guò)并給到服務(wù),但是如果桶中的令牌數(shù)量不足,那么不會(huì)刪除令牌,而是讓此數(shù)據(jù)包等待。
可以試想一下,當(dāng)流量下降時(shí),令牌桶中的令牌會(huì)逐漸積累,這樣如果突然出現(xiàn)高并發(fā),那么就能在短時(shí)間內(nèi)拿到大量的令牌。
固定時(shí)間窗口算法
我們可以對(duì)某一個(gè)時(shí)間段內(nèi)的請(qǐng)求進(jìn)行統(tǒng)計(jì)和計(jì)數(shù),比如在14:15~14:16這一分鐘內(nèi),請(qǐng)求量不能超過(guò)100,也就是一分鐘之內(nèi)不能超過(guò) 100次請(qǐng)求,那么就可以像下面這樣進(jìn)行劃分:
雖然這種模式看似比較合理,但是試想一下這種情況:14:15:59的時(shí)候來(lái)了100個(gè)請(qǐng)求、14:16:01的時(shí)候又來(lái)了100個(gè)請(qǐng)求
出現(xiàn)上面這種情況,符合固定時(shí)間窗口算法的規(guī)則,所以這 200個(gè)請(qǐng)求都能正常接受,但是,你應(yīng)該發(fā)現(xiàn)了,我們其實(shí)希望的是 60秒內(nèi)只有100個(gè)請(qǐng)求,但是這種情況卻是在 3秒內(nèi)出現(xiàn)了200個(gè)請(qǐng)求,很明顯已經(jīng)違背了我們的初衷。
因此,當(dāng)遇到臨界點(diǎn)時(shí),固定時(shí)間窗口算法存在安全隱患。
滑動(dòng)時(shí)間窗口算法
相對(duì)于固定窗口算法,滑動(dòng)時(shí)間窗口算法更加靈活,它會(huì)動(dòng)態(tài)移動(dòng)窗口,重新進(jìn)行計(jì)算:
雖然這樣能夠避免固定時(shí)間窗口的臨界問(wèn)題,但是這樣顯然是比固定窗口更加耗時(shí)的。
回到目錄…
🎀2.2 流控規(guī)則
了解完了我們的限流策略和判定方法之后,我們?cè)?Sentinel 中進(jìn)行實(shí)際測(cè)試一下,打開管理頁(yè)面的簇點(diǎn)鏈路模塊 -> 流控:
這里演示對(duì)我們的圖書接口進(jìn)行限流,點(diǎn)擊流控,會(huì)看到讓我們添加流控規(guī)則:
-
閾值類型:QPS就是每秒鐘的請(qǐng)求數(shù)量,并發(fā)線程數(shù)是按服務(wù)當(dāng)前使用的線程數(shù)據(jù)進(jìn)行統(tǒng)計(jì)的。
-
流控模式:當(dāng)達(dá)到閾值時(shí),流控的對(duì)象,這里暫時(shí)只用直接。
直接:只針對(duì)于當(dāng)前接口。
關(guān)聯(lián):當(dāng)其他接口超過(guò)閾值時(shí),會(huì)導(dǎo)致當(dāng)前接口被限流。
鏈路:更細(xì)粒度的限流,能精確到具體的方法。
-
流控效果:就是我們上面所說(shuō)的三種方案。
回到目錄…
🎀2.3 @SentinelResource 對(duì)方法流控
上面我們學(xué)過(guò)的流控規(guī)則都是對(duì)某個(gè)接口進(jìn)行的限流,這里我們講解一下@SentinelResource 的使用,對(duì)某個(gè)方法進(jìn)行限流。
①我們先在控制層新增一個(gè)接口,但實(shí)際上和之前的接口內(nèi)容一樣,調(diào)用同樣的方法。
②@SentinelResource 監(jiān)控此方法,無(wú)論被誰(shuí)執(zhí)行都在監(jiān)控范圍內(nèi),這里給的 value 是自定義名稱,這個(gè)注解可以加在任何方法上,包括 Controller 中的請(qǐng)求映射方法,跟 @HystrixCommand 賊像
③我們這里創(chuàng)建兩個(gè)請(qǐng)求映射,都來(lái)調(diào)用 Service 的被監(jiān)控方法,需要配置它們單獨(dú)控制:
spring:application:name: bookservicecloud:sentinel:transport:dashboard: localhost:8858# 關(guān)閉 Context 收斂,這樣被監(jiān)控方法可以進(jìn)行不同鏈路的單獨(dú)控制web-context-unify: false④然后我們?cè)?Sentinel 控制臺(tái)中對(duì)該方法添加流控規(guī)則:
最后我們?cè)跒g覽器中對(duì) /book/{bid} 和 /book2/{bid} 這兩個(gè)接口都進(jìn)行測(cè)試,會(huì)發(fā)現(xiàn)無(wú)論請(qǐng)求哪個(gè)接口,只要調(diào)用了 Service 中的getBookById(int bid) 這個(gè)方法,都會(huì)被限流。
回到目錄…
🎀2.4 示例:對(duì)接口流控
①Q(mào)PS 閾值為1,流控模式: 直接,流控效果: 快速失敗。
當(dāng)我們快速地進(jìn)行請(qǐng)求時(shí) (每秒超過(guò)1次),會(huì)直接返回失敗信息:
②QPS 閾值為3,流控模式: 直接,流控效果: Warm up 緩存預(yù)熱。
這樣的緩存預(yù)熱可以在10秒內(nèi),將單機(jī)閾值提升到每秒3次。
③QPS 閾值為1,流控模式: 關(guān)聯(lián) /error,流控效果: 快速失敗。
當(dāng)我們快速地請(qǐng)求自帶的/error接口時(shí) (每秒超過(guò)1次),會(huì)關(guān)聯(lián)的影響到我們的/book接口。
我們使用 PostMan 的 Runner 連續(xù)對(duì)關(guān)聯(lián)資源發(fā)起請(qǐng)求:
開啟Postman,然后我們會(huì)發(fā)現(xiàn)圖書服務(wù)已經(jīng)涼涼。當(dāng)我們關(guān)閉掉 Postman 的任務(wù)后,恢復(fù)正常。
④QPS 閾值為1,流控模式: 鏈路,流控效果: 快速失敗。
最后我們來(lái)講解一下鏈路模式,它能夠更加精準(zhǔn)的進(jìn)行流量控制。鏈路流控模式指的是當(dāng)從指定接口過(guò)來(lái)的資源請(qǐng)求達(dá)到限流條件時(shí),開啟限流。
我們?cè)谏厦鎸W(xué)習(xí)了@SentinelResource 對(duì)方法流控,這種方案會(huì)對(duì)調(diào)用該方法的 /book/{uid} 和 /book2/{uid} 都進(jìn)行限流。
而這個(gè)鏈路選項(xiàng)實(shí)際上就是決定只限流從哪個(gè)方向來(lái)的調(diào)用,比如我們只對(duì)/book2這個(gè)接口對(duì)getBook(int uid) 方法的調(diào)用進(jìn)行限流,那么我們就可以為其指定鏈路:
此時(shí)我們會(huì)發(fā)現(xiàn),限流效果只對(duì)我們配置的鏈路接口有效,而其他鏈路是不會(huì)被限流的。
回到目錄…
🎀2.5 系統(tǒng)規(guī)則
除了直接對(duì)接口進(jìn)行限流規(guī)則控制之外,我們也可以根據(jù)當(dāng)前系統(tǒng)的資源使用情況,決定是否進(jìn)行限流:
系統(tǒng)規(guī)則支持以下的模式:
- Load 自適應(yīng)(僅對(duì) Linux/Unix-like 機(jī)器生效):系統(tǒng)的 load1 作為啟發(fā)指標(biāo),進(jìn)行自適應(yīng)系統(tǒng)保護(hù)。當(dāng)系統(tǒng) load1 超過(guò)設(shè)定的啟發(fā)值,且系統(tǒng)當(dāng)前的并發(fā)線程數(shù)超過(guò)估算的系統(tǒng)容量時(shí)才會(huì)觸發(fā)系統(tǒng)保護(hù)(BBR 階段)。系統(tǒng)容量由系統(tǒng)的 maxQps * minRt 估算得出。設(shè)定參考值一般是 CPU cores * 2.5。
- CPU usage(1.5.0+ 版本):當(dāng)系統(tǒng) CPU 使用率超過(guò)閾值即觸發(fā)系統(tǒng)保護(hù)(取值范圍 0.0-1.0),比較靈敏。
- 平均 RT:當(dāng)單臺(tái)機(jī)器上所有入口流量的平均 RT 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù),單位是毫秒。
- 并發(fā)線程數(shù):當(dāng)單臺(tái)機(jī)器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
- 入口 QPS:當(dāng)單臺(tái)機(jī)器上所有入口流量的 QPS 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
回到目錄…
三、熱點(diǎn)參數(shù)限流
我們還可以對(duì)某一熱點(diǎn)數(shù)據(jù)進(jìn)行精準(zhǔn)限流,比如在某一時(shí)刻,不同參數(shù)被攜帶訪問(wèn)的頻率是不一樣的:
- http://localhost:8081/param?a=10 訪問(wèn)100次
- http://localhost:8081/param?b=10 訪問(wèn)0次
- http://localhost:8081/param?c=10 訪問(wèn)3次
由于攜帶參數(shù)a的請(qǐng)求比較多,我們就可以只對(duì)攜帶參數(shù)a的請(qǐng)求進(jìn)行限流。
①這里我們創(chuàng)建一個(gè)新的測(cè)試請(qǐng)求映射:
@RestController public class ParamController {@GetMapping("/param")@SentinelResource("getParam") // 必須添加該注解,才能設(shè)置參數(shù)精準(zhǔn)限流public String getParamABC(@RequestParam(value = "a", required = false) String a,@RequestParam(value = "b", required = false) String b,@RequestParam(value = "c", required = false) String c) {return "{a = " + a + ", b = " + b + ", c = " + c + "}";} }②啟動(dòng)之后,我們?cè)?Sentinel 的熱點(diǎn)規(guī)則模塊進(jìn)行熱點(diǎn)配置:
③然后開始訪問(wèn)我們的測(cè)試接口,分別攜帶3個(gè)參數(shù)進(jìn)行超閾值請(qǐng)求:
④除了直接對(duì)某個(gè)參數(shù)精準(zhǔn)限流外,我們還可以對(duì)參數(shù)攜帶的指定值單獨(dú)設(shè)定閾值。
?比如我們可以對(duì)參數(shù)b限流閾值放寬到 20,但當(dāng)參數(shù)b=8時(shí),QPS 達(dá)到1就限流:
這樣,將按照我們指定的特例進(jìn)行限流
回到目錄…
四、服務(wù)熔斷和降級(jí)
還記得我們前所說(shuō)的服務(wù)降級(jí)嗎,也就是說(shuō)我們需要在整個(gè)微服務(wù)調(diào)用鏈路出現(xiàn)問(wèn)題的時(shí)候,及時(shí)對(duì)服務(wù)進(jìn)行降級(jí),以防止問(wèn)題進(jìn)一步惡化。
那么,各位是否有思考過(guò),如果在某一時(shí)刻,服務(wù)B 出現(xiàn)故障(可能就卡在那里了),而這時(shí)服務(wù)A 依然有大量的請(qǐng)求,在調(diào)用服務(wù)B,那么,由于服務(wù)A 沒(méi)辦法再短時(shí)間內(nèi)完成處理,新來(lái)的請(qǐng)求就會(huì)導(dǎo)致線程數(shù)不斷地增加,這樣,CPU 的資源很快就會(huì)被耗盡。
那么要防止這種情況,就只能進(jìn)行隔離了,這里我們提兩種隔離方案:
線程池隔離實(shí)際上就是對(duì)每個(gè)服務(wù)的遠(yuǎn)程調(diào)用單獨(dú)開放線程池,比如服務(wù)A 要調(diào)用服務(wù)B,那么只基于固定數(shù)量的線程池,這樣即使在短時(shí)間內(nèi)出現(xiàn)大量請(qǐng)求,由于沒(méi)有線程可以分配,所以就不會(huì)導(dǎo)致資源耗盡了。相當(dāng)于讓不同的服務(wù)調(diào)用,去各自的線程池中獲取資源去請(qǐng)求,從而不會(huì)消耗其它服務(wù)調(diào)用的資源 和 CPU的資源。
信號(hào)量隔離是使用 Semaphore 類實(shí)現(xiàn)的(如果不了解,可以觀看本系列 并發(fā)編程篇 視頻教程),思想基本上與上面是相同的,也是限定指定的線程數(shù)量能夠同時(shí)進(jìn)行服務(wù)調(diào)用,但是它相對(duì)于線程池隔離,開銷會(huì)更小一些,使用效果同樣優(yōu)秀,也支持超時(shí)等。
Sentinel 也正是采用的這種方案實(shí)現(xiàn)隔離的。
好了,說(shuō)回我們的熔斷和降級(jí),當(dāng)下游服務(wù)因?yàn)槟撤N原因變得不可用或響應(yīng)過(guò)慢時(shí),上游服務(wù)為了保證自己整體服務(wù)的可用性,不再繼續(xù)調(diào)用目標(biāo)服務(wù)而是快速返回或是執(zhí)行自己的替代方案,這便是服務(wù)降級(jí)。
整個(gè)過(guò)程分為三個(gè)狀態(tài):
- 關(guān)閉:熔斷器不工作,所有請(qǐng)求全部該干嘛干嘛。
- 打開:熔斷器工作,所有請(qǐng)求一律降級(jí)處理。
- 半開:嘗試進(jìn)行一下下正常流程,要是還不行繼續(xù)保持打開狀態(tài),否則關(guān)閉。
那么我們來(lái)看看 Sentinel 中如何進(jìn)行熔斷和降級(jí)操作,打開管理頁(yè)面,我們可以自由新增熔斷規(guī)則,其中熔斷策略有三種模式:
回到目錄…
💎4.1 熔斷規(guī)則:慢調(diào)用比例
慢調(diào)用比例:如果出現(xiàn)那種半天都處理不完的調(diào)用,有可能就是服務(wù)出現(xiàn)故障,導(dǎo)致卡頓,這個(gè)選項(xiàng)是按照最大響應(yīng)時(shí)間(RT)進(jìn)行判定。如果一次請(qǐng)求的處理時(shí)間超過(guò)了指定的 RT,那么就被判定為慢調(diào)用。在一個(gè)統(tǒng)計(jì)時(shí)長(zhǎng)內(nèi),如果請(qǐng)求數(shù)目大于最小請(qǐng)求數(shù)目,并且被判定為慢調(diào)用的請(qǐng)求比例已經(jīng)超過(guò)閾值,將觸發(fā)熔斷。經(jīng)過(guò)熔斷時(shí)長(zhǎng)之后,將會(huì)進(jìn)入到半開狀態(tài)進(jìn)行試探(這里和Hystrix一致)
然后創(chuàng)建一個(gè)接口,來(lái)模擬一下慢調(diào)用:
@GetMapping("/slow") public String slow() throws InterruptedException {Thread.sleep(500);return "這是一個(gè)慢比例調(diào)用!"; }重啟,然后我們創(chuàng)建一個(gè)新的熔斷規(guī)則:
可以看到,熔斷規(guī)則內(nèi)是可以正常訪問(wèn)的:
但是突破熔斷規(guī)則時(shí),就會(huì)服務(wù)熔斷:
回到目錄…
💎4.2 熔斷規(guī)則:異常比例
異常比例:這個(gè)與慢調(diào)用比例類似,不過(guò)這里判斷的是出現(xiàn)異常的次數(shù),與上面一樣,我們也來(lái)進(jìn)行一些小測(cè)試:
啟動(dòng)服務(wù)器,接著添加我們的熔斷規(guī)則:
現(xiàn)在我們進(jìn)行訪問(wèn),會(huì)發(fā)現(xiàn)后臺(tái)瘋狂報(bào)錯(cuò),然后就熔斷了:
回到目錄…
💎4.3 熔斷規(guī)則:異常數(shù)
異常數(shù):這個(gè)和上面的唯一區(qū)別就是,只要達(dá)到指定的異常數(shù)量,就熔斷,這里我們修改一下熔斷規(guī)則:
現(xiàn)在我們?cè)俅尾粩嘣L問(wèn)此接口,可以發(fā)現(xiàn),效果跟之前其實(shí)是差不多的,只是判斷的策略稍微不同罷了:
回到目錄…
💎4.4 精準(zhǔn)服務(wù)降級(jí)
同樣,Sentinel 也支持我們進(jìn)行方法級(jí)別細(xì)粒度的熔斷降級(jí)。
我們?cè)谛枰刂频姆椒ㄉ咸砑?@SentinelResource 注解,可以配置 blockHandler 參數(shù)返回熔斷后的代替方案。
接著我們對(duì)進(jìn)行熔斷配置,注意是對(duì)我們添加的@SentinelResource中指定名稱的testMethod進(jìn)行配置:
OK,可以看到熔斷降級(jí)之后的效果:
回到目錄…
💎4.5 遠(yuǎn)程調(diào)用中的服務(wù)熔斷
我們上面學(xué)的都是在一個(gè)微服務(wù)中,對(duì)接口或方法訪問(wèn)時(shí),進(jìn)行了降級(jí)和熔斷。那此時(shí),我們還需要知道在微服務(wù)之間的遠(yuǎn)程調(diào)用過(guò)程中,某個(gè)微服務(wù)出現(xiàn)問(wèn)題導(dǎo)致無(wú)法訪問(wèn)時(shí),我們?cè)撊绾巫鋈蹟嗪徒导?jí)呢?
我們以 borrowservice 的 /borrow/{uid} 接口中的遠(yuǎn)程調(diào)用為例進(jìn)行講解吧!
方案一:OpenFeign 實(shí)現(xiàn)降級(jí)熔斷:
- Feign 集成了 Spring Cloud CircuitBreaker斷路器,實(shí)現(xiàn)了熔斷降級(jí)的功能。
- 但是,之前我們?cè)谑褂脮r(shí)發(fā)現(xiàn),Feign 的降級(jí)需要依靠 Hystrix 組件來(lái)實(shí)現(xiàn),所以那時(shí)我們的啟動(dòng)類依然需要用 @EnableHystrix 注解來(lái)修飾。
參考往期文章:SpringCloud之 Hystrix服務(wù)熔斷 的 OpenFeign 實(shí)現(xiàn)降級(jí) 模塊 - 其實(shí),我們今天學(xué)習(xí)的 sentinel 也支持 feign 的使用,這樣我們就不再需要廢棄的 Hystrix 組件了。
①首先我們需要在配置文件中開啟 sentinel 對(duì) feign 的支持:
feign:sentinel:enabled: true②我們給 borrowservice 中的 BookClient 接口創(chuàng)建替代實(shí)現(xiàn)類:
@Component public class BookClientFallback implements BookClient {@Overridepublic Book findBookById(int bid) {return new Book(-1, "代替方案", "");} }③為 BookClient 接口指定替代方案,使用 @FeignClient 注解的 fallback 屬性
@FeignClient(value = "bookservice", fallback = BookClientFallback.class) public interface BookClient {@GetMapping("/book/{bid}")Book findBookById(@PathVariable("bid") int bid); }④然后直接啟動(dòng)就可以了,中途的時(shí)候我們把圖書服務(wù)全部下掉,可以看到正常使用替代方案:
這樣 Feign 的配置就OK了,
方案二:@SentinelRestTemplate 實(shí)現(xiàn)降級(jí)熔斷:
原本傳統(tǒng)的 RestTemplate 需要借助 @SentinelRestTemplate注解實(shí)現(xiàn):
@Bean @LoadBalanced @SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class,fallback = "fallback", fallbackClass = ExceptionUtil.class) // 這里同樣可以設(shè)定 fallback 等參數(shù) public RestTemplate restTemplate() {return new RestTemplate(); }這里就不多做贅述了。
回到目錄…
五、限流 / 熔斷后的異常處理
現(xiàn)在我們已經(jīng)了解了如何進(jìn)行限流、服務(wù)熔斷降級(jí)操作,那么這些狀態(tài)下的返回結(jié)果該怎么修改呢,我們看到返回的是 Sentinel 默認(rèn)的數(shù)據(jù),現(xiàn)在我們希望自定義改如何操作?不僅僅是限流、服務(wù)降級(jí)熔斷,只要是 Sentinel 包括的異常都可以處理。
🔑5.1 接口異常處理
這里我們先創(chuàng)建好被限流狀態(tài)下需要返回的內(nèi)容,定義一個(gè)請(qǐng)求映射:
@Controller public class BlockedController {@RequestMapping("/blocked")public JSONObject blocked(){JSONObject object = new JSONObject();object.put("code", 403);object.put("success", false);object.put("massage", "您的請(qǐng)求頻率過(guò)快,請(qǐng)稍后再試!");return object;} }接著我們?cè)谂渲梦募袑⒋隧?yè)面設(shè)定為限流頁(yè)面:
spring:cloud:sentinel:transport:dashboard: localhost:8858# 將剛剛編寫的請(qǐng)求映射設(shè)定為限流頁(yè)面block-page: /blocked默認(rèn)的返回結(jié)果:
修改后,當(dāng)被限流時(shí),就會(huì)被重定向到指定頁(yè)面:
回到目錄…
🔑5.2 方法中 sentinel 異常的處理
那么,對(duì)于方法級(jí)別的限流呢?經(jīng)過(guò)前面的學(xué)習(xí)我們知道,當(dāng)某個(gè)方法被限流時(shí),會(huì)直接在后臺(tái)拋出異常,那么這種情況我們?cè)撛趺刺幚砟?#xff1f;
比如我們之前在 Hystrix 中可以直接添加一個(gè)替代方案,@HystrixCommand(fallbackMethod = "onError"),這樣當(dāng)出現(xiàn)異常時(shí)會(huì)直接執(zhí)行我們的替代方法并返回,Sentinel 也可以。
比如我們還是在 getBookById(int bid) 方法上進(jìn)行配置:注意blockHandler只能處理限流情況下拋出的異常
@Override @SentinelResource(value = "getBook", blockHandler = "blocked") //監(jiān)控此方法,無(wú)論被誰(shuí)執(zhí)行都在監(jiān)控范圍內(nèi),這里給的value是自定義名稱,這個(gè)注解可以加在任何方法上,包括Controller中的請(qǐng)求映射方法,跟HystrixCommand賊像 public Book getBookById(int bid) {return bookMapper.getBookById(bid); }// 替代方案,注意參數(shù)和返回值需要保持一致,并且參數(shù)最后還需要額外添加一個(gè) BlockException public Book blocked(int bid, BlockException e) {return new Book(-1, "", ""); }可以看到,一旦被限流將執(zhí)行替代方案,最后返回的結(jié)果就是:
回到目錄…
🔑5.3 方法中其它異常的處理
如果是方法本身拋出的其他類型異常,不在blockHandler管控范圍內(nèi),但是可以通過(guò)@SentinelResource的其它參數(shù)進(jìn)行處理:
@RestController public class FallbackController {@GetMapping("/test")@SentinelResource(value = "testFallback",fallback = "except", // 指定異常時(shí)的替代方案blockHandler = "block", // 出現(xiàn)限流時(shí)的替代方案exceptionsToIgnore = IOException.class) // 可以忽視的異常,出現(xiàn)該異常時(shí)不使用替代方案public String testFallback() {throw new RuntimeException("出現(xiàn)了運(yùn)行時(shí)異常");}// 替代方法必須和原方法返回值和參數(shù)一致,最后可以添加一個(gè)Throwable作為參數(shù)接受異常public String except(Throwable t) {return t.getMessage();} }我們給剛剛作了異常處理的方法,設(shè)置了流控:
這樣,其它的異常也可以有替代方案了:
同樣的,當(dāng)超閾值訪問(wèn)時(shí),Sentinel 的限流異常也可以處理:
特別注意:
- 如果在沒(méi)有配置 blockHandler 的情況下,fallback 會(huì)將 Sentinel 機(jī)制內(nèi)(限流、熔斷)的異常也一并處理了。
- 如果配置了 blockHandler,那么在出現(xiàn)限流/熔斷時(shí),依然只會(huì)執(zhí)行 blockHandler指定的替代方案(因?yàn)橄蘖魇窃诜椒▓?zhí)行之前進(jìn)行的)。
回到目錄…
總結(jié):
提示:這里對(duì)文章進(jìn)行總結(jié):
本文是對(duì)Sentinel 流量防衛(wèi)兵的學(xué)習(xí),學(xué)習(xí)了 Sentinel 的安裝與部署,還使用了它提供的功能:流量控制、服務(wù)降級(jí)熔斷,并且還可以自定義sentinel異常的處理方案。之后的學(xué)習(xí)內(nèi)容將持續(xù)更新!!!
總結(jié)
以上是生活随笔為你收集整理的SpringCloud Alibaba之 Sentinel流量防卫兵的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 锁相环(单相+陷波器)入门理解
- 下一篇: 项目申请书一点总结经验