使用Sentinel實現(xiàn)流控和服務降級的代碼示例
伴隨著微服務的興起,服務間的調(diào)用的可靠性也越發(fā)重要了起來,在下面的這種服務調(diào)用關系中:
1.服務調(diào)用關系圖
A服務調(diào)用服務C,B服務調(diào)用服務D,服務C/D調(diào)用服務E,而服務會進行數(shù)據(jù)庫讀寫,如果服務E在服務C/D的頻繁調(diào)用下不堪重負,使得服務E宕掉了,則將導致整個服務集群不可用,這種現(xiàn)象簡稱為服務雪崩,因此為了避免這種情況,就需要額外的技術保證服務的可用性。如常用的hystrix
和sentinel
就是用來處理這種情況的。前者是 Spring Cloud Netflix 常使用的組件,而后者是 Spring Cloud Alibaba 經(jīng)常使用的組件。本文以介紹 sentinel
為主。
本文主要有以下內(nèi)容:
Sentinel
組件的簡單介紹Sentinel
流控規(guī)則Sentinel
服務降級方案
什么是Sentinel
隨著微服務的流行,服務和服務之間的穩(wěn)定性變得越來越重要。Sentinel 是面向分布式、多語言異構化服務架構的流量治理組件,主要以流量為切入點,從流量路由、流量控制、流量整形、熔斷降級、系統(tǒng)自適應過載保護、熱點流量防護等多個維度來幫助開發(fā)者保障微服務的穩(wěn)定性。
服務雪崩現(xiàn)象的發(fā)生,一般有兩種原因,一種是外部的大量請求調(diào)用,如在上面的例子中服務C/D頻繁調(diào)用服務E,導致服務E不堪重負,另外一種是自身的原因,如服務E內(nèi)部發(fā)生了不可預知的錯誤,如數(shù)數(shù)據(jù)庫讀寫效率過低,亦或者自身業(yè)務邏輯代碼拋出了異常等。
為了解決這兩種情況,Sentinel
提供了針對外部的流量整形和針對本身的服務異常服務容錯治理。
針對外部原因:提供了三種流控規(guī)則和三種流控效果來進行服務限流。
- 流控規(guī)則
- 直接流控
- 關聯(lián)流控
- 鏈路流控
- 流控效果
- 快速失敗
- Warm Up
- 排隊等待
針對內(nèi)部原因:提供了服務降級方案和服務熔斷的方式來保證服務的穩(wěn)定性。
- 服務降級:針對單次服務調(diào)用,如服務C調(diào)用服務E時,服務E的方法出現(xiàn)了異常導致無法完成正常的處理邏輯,如響應超時、服務異常,此時我們給服務C提供一個默認的返回值,保證此次調(diào)用得到處理。
- 服務熔斷:指服務的異常調(diào)用占到一定的數(shù)量或者比例,達到了我們的判定條件,如在一個統(tǒng)計的時間窗口內(nèi),服務異常比例占比到達70%,觸發(fā)了服務熔斷,即所有的后續(xù)請求直接進入降級邏輯,不再調(diào)用目標訪問方法。
對外部流量的控制
首先搭建sentinel
的運行環(huán)境:
在 Sentinel官網(wǎng)頁面下載可執(zhí)行的jar文件,版本sentinel-dashboard-1.8.2.jar
。下載完成后通過命令 java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar
啟動 Sentinel
。通過 127.0.0.1:8080
訪問 sentinel
控制臺,默認的賬戶密碼均為sentinel
接著在項目中集成sentinel
在pom.xml
添加下列依賴
<dependency> ? ?<groupId>com.alibaba.cloud</groupId> ? ?<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
在application.yml
中添加如下配置
spring: cloud: ? sentinel: ? ? transport: ? ? ? ?# sentinel api端口,默認8719 ? ? ? port: 8719 ? ? ? ?# dashboard地址 ? ? ? dashboard: localhost:8080
在ConsumerController
添加如下代碼
@GetMapping("sentinel") @SentinelResource( ? ?value = "sentinel", ? ?blockHandler = "consumerSentinelResource_Blocked", ? ?fallback = "consumerSentinelResource_fallback") public String consumerSentinelResource() { ?// if (Math.random() > 0.6 ){ ?// ? ? System.out.println("執(zhí)行fallback降級邏輯"); ?// ? ? throw new RuntimeException("RuntimeException"); ?// } ? ?return "正常返回值"; } ? public String consumerSentinelResource_Blocked(BlockException blockException){ ? ?System.out.println("執(zhí)行BlockException降級邏輯"); ? ?return new Date().toLocaleString() + "BlockException: 服務被降級"; } ? public String consumerSentinelResource_fallback(){ ? ?return new Date().toLocaleString() + ?"通用異常解決方法: 服務被降級"; }
@SentinelResource
注解表明這是一個Sentinel
資源。
value
屬性:資源名稱必須填寫。blockHandler
:處理因觸發(fā)流控規(guī)則的請求拋出的BlockException
,執(zhí)行的的降級方法
流控規(guī)則
啟動 sentinel
并登錄控制臺后,側邊欄初始狀態(tài)下并沒有我們的服務,這時需要我們訪問一下服務,隨便調(diào)用一個服務接口,側邊欄就會出現(xiàn)我們注冊的服務。
2.sentinel控制臺截圖
點開我們的服務,在側邊欄有個流控規(guī)則按鈕,點擊流控規(guī)則,在頁面的右上角有一個新增流控規(guī)則按鈕,點擊此按鈕,就可以新增流控規(guī)則。
直接流控
直接流控是我們常用的流控,一般情況下直接添加即可。按照下圖添加流控規(guī)則:
3.直接流控截圖
規(guī)則說明:在每秒請求數(shù)超過1個時,就觸發(fā)流控規(guī)則,即執(zhí)行consumerSentinelResource_Blocked()
方法的邏輯,通過postman
調(diào)試接口即可得到下圖的結果:
4.觸發(fā)直接流控規(guī)則
關聯(lián)流控
如果兩個資源之間存在競爭關系,如共享一個線程池或者數(shù)據(jù)庫連接池,這時候就可以使用關聯(lián)流控,此時在低優(yōu)先級的資源上設置流控規(guī)則,進而使得高優(yōu)先級的資源獲得競爭優(yōu)勢,
例如:資源A和資源B存在競爭關系,此時在資源A上設置流控規(guī)則和判斷閾值,此時當高優(yōu)先級的請求數(shù)量達到判斷閾值時,就會對低優(yōu)先級的資源進行流控,
在ConsumerController
添加如下代碼:
@SentinelResource(value = "condition") @GetMapping("condition") public String condition() { ? ?return "condition: 我是高優(yōu)先級的資源"; }
在控制臺添加如下規(guī)則
5.配置關聯(lián)流控規(guī)則
ps:通過postman
工具怎么都無法出現(xiàn)低優(yōu)先級服務降級的響應,于是乎我使用了一個python腳本模擬http請求,關聯(lián)流控規(guī)則就起作用了!腳本內(nèi)容如下
import requests url = "http://127.0.0.1:10001/consumer/condition" url2 = "http://127.0.0.1:10001/consumer/sentinel" while(True): ? ?res = requests.get(url=url) ? ?res2 = requests.get(url=url2) ? ?print("condition response:",res.text) ?# 返回請求結果 ? ?print("sentinel response:",res2.text) ?# 返回請求結果 ? ?print("------------------------------")
響應結果如下圖
6.關聯(lián)流控規(guī)則
鏈路流控
在一個應用中,對同一個資源有多條不同的訪問路徑時,如果需要對訪問路徑限流,則可以選用鏈路流控。如下圖:
7.鏈路流控示意圖
query 和 query2 都能訪問到 resource, 如果想要對api/query2
進行流控,則此時可以選擇鏈路流控的方式。具體配置如下
8.配置鏈路流控規(guī)則
流控效果
快速失敗
在前面的示例中,所有的流控效果都是快速失敗,快速失敗是sentinel
默認的流控效果,即到達我們的設置的閾值之后就失敗。
Warm Up
這種方式實現(xiàn)了預熱的效果,即在設定的預熱時間內(nèi),閾值從起始閾值逐漸提升到我們設定的閾值,起始閾值的值為設定的 單機閾值 / 冷加載因子,sentinel
默認的冷加載因子為 3。
9.Warm Up配置
以上圖的配置,則流控效果在 10s 從 100 / 3 = 33 緩慢提升到 100。
內(nèi)部降級邏輯
降級方案
流控規(guī)則和流控效果是作用在外部流量的,而對自身服務而言,當服務調(diào)用出現(xiàn)足夠多的異常時,為保證服務的穩(wěn)定性,可采取服務熔斷策略,服務熔斷是多次異常調(diào)用的累積結果。
在上面的示例代碼中,我們在使用@SentinelResource
注解時,配置了blockHandler、 fallback
這兩個屬性, 這兩個屬性的值都是編寫降級邏輯的,但是稍有不同。
blockHandler
只能處理服務拋出的異常時BlockException
的情況,BlockException
是Sentinel
組件配置的流控規(guī)則起作用時拋出的異常,即被當請求被流控規(guī)則攔截時,拋出的異常。
public String consumerSentinelResource_Blocked(BlockException blockException){ ? ?String s = new Date().toLocaleString() + "BlockException: 服務被降級"; ? ?return s; }
在編寫blockHandler
的方法時,需要在參數(shù)列表的最后添加一個BlockException
類型的參數(shù),否則降級邏輯不會起作用。
fallback
使用場景是當調(diào)用的方法拋出了除BlockException
類型的其他運行時異常,如代碼中分子為 0 拋出的java.lang.ArithmeticException: / by zero
。
fallback
適用于編寫通用的降級邏輯,fallback
方法的編寫不需要添加額外參數(shù),和 controller 中的方法形參保持一致即可。
熔斷策略
在sentinel
中,定義了三種熔斷策略:
異常比例
在統(tǒng)計時長指定的時間內(nèi),異常調(diào)用比例超過設置的比例閾值,且請求數(shù)滿足設置的最小請求數(shù),則進行服務熔斷,直接進入降級邏輯。如果請求數(shù)沒有滿足設置的最小請求數(shù),則即使高于這個比例也不會熔斷。如最小請求數(shù)是 10,比例為 0.5,即使前 9 次都失敗了,也不會熔斷。
異常數(shù)
在統(tǒng)計時長指定的時間內(nèi),異常調(diào)用比例大于設置的請求數(shù),且請求數(shù)滿足設置的最小請求數(shù),則進行服務熔斷,直接進入降級邏輯。這里大于指的是加入設置的異常數(shù)是3,則熔斷發(fā)生在第四次失敗調(diào)用之后。
慢調(diào)用比例
在統(tǒng)計時間窗口內(nèi),請求響應的時間大于了設置的 最大RT 時間的請求數(shù)所占全部請求數(shù)的比例超過了設置的比例閾值,且總請求數(shù)大于等于最小請求數(shù),則觸發(fā)熔斷規(guī)則。
最大RT是判斷慢請求的條件,不是觸發(fā)熔斷的條件。
熔斷狀態(tài)的轉(zhuǎn)換
Sentinel
的熔斷器有三個狀態(tài):關閉,全開,半開,在沒有達到觸發(fā)熔斷的條件時,熔斷器處于關閉狀態(tài),當達到熔斷開始的條件時,熔斷器出處于全開狀態(tài),當熔斷時長結束時,熔斷器不會立馬關閉,而是處于半開狀態(tài),此時,下一個請求的狀態(tài)決定了是關閉還是全開,如果下一個請求請求成功,則關閉熔斷器,退出熔斷狀態(tài),如果失敗,則立馬進入熔斷狀態(tài),不需要再次滿足設置的條件。
10.熔斷狀態(tài)轉(zhuǎn)換示意圖
需要說明的是:開源的Sentinel
組件的配置的規(guī)則并沒有被持久化,因此當我們重啟應用或者重啟sentinel
組件,都需要重新配置相關規(guī)則。
以上就是使用Sentinel實現(xiàn)流控和服務降級的代碼示例的詳細內(nèi)容,更多關于Sentinel 流控和服務降級的資料請關注腳本之家其它相關文章!
相關文章
SpringBoot數(shù)據(jù)庫初始化datasource配置方式
這篇文章主要為大家介紹了SpringBoot數(shù)據(jù)庫初始化datasource配置方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12spring中jdbcTemplate.batchUpdate的幾種使用情況
本文主要介紹了spring中jdbcTemplate.batchUpdate的幾種使用情況,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-04-04Spring Boot 的創(chuàng)建和運行示例代碼詳解
Spring Boot 的誕生是為了簡化Spring程序的開發(fā),今天給大家介紹下Spring Boot 的創(chuàng)建和運行,主要包括Spring Boot基本概念和springboot優(yōu)點,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧2022-07-07Spring注解實現(xiàn)循環(huán)重試功能(適用場景分析)
這篇文章主要介紹了Spring注解實現(xiàn)循環(huán)重試功能,本篇主要簡單介紹了Springboot中的Retryable的使用,主要的適用場景和注意事項,當需要重試的時候還是很有用的,需要的朋友可以參考下2023-04-04