详解sentinel:分布式系统的流量防卫兵
目錄
sentinel是什么
sentinel具有以下特征
豐富的應(yīng)用場景
完備的實時監(jiān)控
廣泛的開源生態(tài)
完善的SPI擴(kuò)展點(diǎn)
sentinel的主要特性
?下載安裝
控制臺
應(yīng)用接入
pom文件
application.yml
sentinel的功能
流控規(guī)則
直接-快速失敗
?默認(rèn)錯誤
?關(guān)聯(lián)-快速失敗
預(yù)熱-Warm Up
?排隊等待
降級規(guī)則
RT(平均響應(yīng)時間)
異常比例
異常數(shù)
熱點(diǎn)規(guī)則-熱點(diǎn)key限流
?自定義異常信息
?熱點(diǎn)規(guī)則?
參數(shù)例外項
系統(tǒng)規(guī)則(總控-所有的uri的控制)
自定義兜底方法(錯誤信息)
?@SentinelResource注解
ribbon、nacos、sentinel一起使用
異常忽略
?feign
sentinel、Hystrix、resilience4j的對比
持久化
sentinel是什么
隨著微服務(wù)的流行,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來越重要。Sentinel以流量為切入點(diǎn),從流量控制、熔斷降級、系統(tǒng)負(fù)載保護(hù)等多個維度保護(hù)服務(wù)的穩(wěn)定性。
其實sentinel就是histrix的阿里版本!有一套單獨(dú)的web管理界面,可以給我們進(jìn)行更加細(xì)粒度化的配置流控、速率控制、服務(wù)熔斷、服務(wù)降級等等。
sentinel具有以下特征
豐富的應(yīng)用場景
sentinel承接了阿里巴巴近10年的雙十一大促流量的核心場景,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)、消息削峰填谷、集群流量控制、實時熔斷下游不可用應(yīng)用等。
完備的實時監(jiān)控
sentinel同時提供實時的監(jiān)控功能。您可以在控制臺中看到接入應(yīng)用的單臺機(jī)器秒級數(shù)據(jù),甚至500臺以下規(guī)模的集群的匯總運(yùn)行情況。
廣泛的開源生態(tài)
sentinel提供開箱即用的與其它開源框架/庫的整合模塊,例如與spring cloud、dubbo、gRPC的整合。您只需要引入相應(yīng)的依賴并進(jìn)行簡單的配置即可快速地接入sentinel。
完善的SPI擴(kuò)展點(diǎn)
sentinel提供簡單易用、完善的SPI擴(kuò)展接口。您可以通過實現(xiàn)擴(kuò)展接口來快速地定制邏輯。例如定制規(guī)則管理、適配動態(tài)數(shù)據(jù)源等。
sentinel的主要特性
?下載安裝
控制臺
sentinel分為兩個部分:
1.核心庫(java客戶端)不依賴任何框架/庫,能夠運(yùn)行于所有java運(yùn)行時環(huán)境,同時對dubbo/spring cloud等框架也有較好的支持。
2.?控制臺(dashboard)基于spring?boot開發(fā),打包后可以直接運(yùn)行,不需要額外的tomcat等應(yīng)用容器。
sentinel使用的是8080端口!
啟動命令:java -jar sentinel-dashboard-1.7.0.jar
用戶名密碼:sentinel/sentinel
localhost:8080訪問(懶加載)
?
應(yīng)用接入
pom文件
<!--SpringCloud ailibaba nacos --> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--SpringCloud ailibaba sentinel-datasource-nacos 后續(xù)做持久化用到--> <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId> </dependency> <!--SpringCloud ailibaba sentinel --> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>application.yml
server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服務(wù)注冊中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard地址port: 8719 #默認(rèn)8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口datasource:ds1:nacos:server-addr: localhost:8848dataId: cloudalibaba-sentinel-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flowmanagement:endpoints:web:exposure:include: '*'feign:sentinel:enabled: true # 激活Sentinel對Feign的支持sentinel的功能
流控規(guī)則
1.資源名:唯一名稱,默認(rèn)請求路徑。
2.針對來源:Sentinel可以針對調(diào)用者進(jìn)行限流,填寫微服務(wù)名,默認(rèn)default(不區(qū)分來源)。
3.閾值類型/單機(jī)閾值:
? ? QPS(每秒鐘的請求數(shù)量):當(dāng)調(diào)用該api的QPS達(dá)到閾值的時候,進(jìn)行限流。
? ? 線程數(shù):當(dāng)調(diào)用該api的線程數(shù)達(dá)到閾值的時候,進(jìn)行限流。
4.是否集群:不需要集群。
5.流控模式:
? ? 直接:api達(dá)到限流條件時,直接限流。
? ? 關(guān)聯(lián):當(dāng)關(guān)聯(lián)的資源達(dá)到閾值時,就限流自己。(當(dāng)與A關(guān)聯(lián)的資源B達(dá)到閾值后,就限流A自己)
? ? 鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進(jìn)來的流量,如果達(dá)到閾值,就進(jìn)行限流)【api級別的針對來源】
6.流控效果:
? ? 快速失敗:直接失敗,拋出異常。
? ? Warm?Up(預(yù)熱):根據(jù)codeFactor(冷加載因子,默認(rèn)3)的值,從閾值/codeFactor,經(jīng)過預(yù)熱時長,才達(dá)到設(shè)置的QPS閾值。
? ? 排隊等待:勻速等待,讓請求以勻速的速度通過,閾值類型必須設(shè)置為QPS,否則無效。
直接-快速失敗
以下配置表示:1秒鐘內(nèi)只可以查詢一次,若超過次數(shù)1,就直接-快速失敗,報默認(rèn)錯誤。
?默認(rèn)錯誤
sentinel有自帶默認(rèn)的錯誤
源碼:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
直接:快速失敗(默認(rèn)的流控處理)。
?關(guān)聯(lián)-快速失敗
以下配置表示:當(dāng)關(guān)聯(lián)資源/testB的qps閾值超過1時,就限流/testA的Rest訪問地址,當(dāng)關(guān)聯(lián)資源到閾值后限制配置好的資源名。
預(yù)熱-Warm Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即預(yù)熱/冷啟動方式。當(dāng)系統(tǒng)長期處于低水位的情況下,當(dāng)流量突然增加時,直接把系統(tǒng)拉升到高水位可能瞬間把系統(tǒng)壓垮。通過“冷啟動”,讓通過的流量緩慢增加,在一定時間內(nèi)逐漸增加到閾值上限,給冷系統(tǒng)一個預(yù)熱的時間,避免冷系統(tǒng)被壓垮。
通常冷啟動的過程系統(tǒng)允許通過的QPS曲線如下圖所示:
?Warm Up配置
默認(rèn)coldFactor為3,即請求QPS從(threshold /?3)開始,經(jīng)多少預(yù)熱時長才逐漸升至設(shè)定的QPS閾值。
案例:閾值為10+預(yù)熱時長設(shè)置5秒。
系統(tǒng)初始化的閾值為10 / 3約等于3,即閾值剛開始為3;然后過了5秒后閾值才慢慢升高恢復(fù)到10。
?排隊等待
勻速排隊,讓請求以均勻的速度通過,閾值類型必須設(shè)成QPS,否則無效。
設(shè)置含義:/testA每秒1次請求,超過的話就排隊等待,等待的超時時間為20000毫秒。
?勻速排隊(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式會嚴(yán)格控制請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應(yīng)的是漏桶算法。
該方式的作用如下圖所示:
?這種方式主要用于處理間隔性突發(fā)的流量,例如消息隊列。想象一下這樣的場景,在某一秒有大量的請求到來,而接下來的幾秒則處于空閑狀態(tài),我們希望系統(tǒng)能夠在接下來的空閑期間逐漸處理這些請求,而不是在第一秒直接拒絕多余的請求。
降級規(guī)則
?
RT(平均響應(yīng)時間,秒級):
? ? 平均響應(yīng)時間?超出閾值?且?在時間窗口內(nèi)通過的請求>=5,兩個條件同時滿足后觸發(fā)降級。
? ? 窗口期過后關(guān)閉斷路器。
? ? RT最大4900(更大的需要通過-Dcsp.sentinel.statistic.max.rt=xxxx才能生效)
異常比例(秒級):
? ? QPS>=5且異常比例(秒級統(tǒng)計)超過閾值時,觸發(fā)降級;時間窗口結(jié)束后,關(guān)閉降級。
異常數(shù)(分鐘級):
? ? 異常數(shù)(分鐘統(tǒng)計)超過閾值時,觸發(fā)降級;時間窗口結(jié)束后,關(guān)閉降級。
?? ?Sentinel熔斷降級會在調(diào)用鏈路中某個資源出現(xiàn)不穩(wěn)定狀態(tài)時(例如調(diào)用超時或異常比例升高),對這個資源的調(diào)用進(jìn)行限制,讓請求快速失敗,避免影響到其它的資源而導(dǎo)致級聯(lián)錯誤。
?? ?當(dāng)資源被降級后,在接下來的降級時間窗口之內(nèi),對該資源的調(diào)用都自動熔斷(默認(rèn)行為是拋出DegradeException)。
?? ?Sentinel是沒有半開狀態(tài)的!半開狀態(tài)系統(tǒng)自動去檢測是否請求有異常,沒有異常就關(guān)閉斷路器恢復(fù)使用,有異常則繼續(xù)打開斷路器不可用。具體可以參考Hystrix。
RT(平均響應(yīng)時間)
?
?
?
?按照上述配置,永遠(yuǎn)一秒鐘打進(jìn)來10個線程(大于5個了)調(diào)用testD,我們希望200毫秒處理完本次任務(wù),如果超過200毫秒還沒處理完,在未來1秒鐘的時間窗口內(nèi),斷路器打開(保險絲跳閘)微服務(wù)不可用,保險絲跳閘斷電了。
后續(xù)我們停止jmeter,沒有這么大的訪問量了,斷路器關(guān)閉(保險絲恢復(fù)),微服務(wù)繼續(xù)使用。
異常比例
異常比例(DEGRADA_GRADE_EXCEPTION_RATIO):當(dāng)資源的每秒請求量>=5,并且每秒異常總數(shù)占通過量的比值超過閾值(DegradeRule中的count)之后,資源進(jìn)入降級狀態(tài),即在接下來的時間窗口(DeradeRule中的timeWindow,以s為單位)之內(nèi),對這個方法的調(diào)用都會自動地返回。異常比率的閾值范圍是[0.0, 1.0],代表0% - 100%。
?
?
?
?按照上述配置,單獨(dú)訪問一次,必然會報錯一次(int age = 10/0),調(diào)用一次報錯一次。
開啟jmeter后,直接高并發(fā)發(fā)送請求,后臺持續(xù)報錯,斷路器開啟,微服務(wù)不可用了。
異常數(shù)
異常數(shù)(DEGRADE_GRADE_EXCEPTION_COUNT):當(dāng)資源近1分鐘的異常數(shù)目超過閾值之后會進(jìn)行熔斷。注意由于統(tǒng)計時間窗口是分鐘級別的,若timeWindow小于60s,則結(jié)束熔斷狀態(tài)后仍可能再進(jìn)入熔斷狀態(tài)。
時間窗口一定要大于等于60秒。
?
?
?http://localhost:8080/testE,第一次訪問報錯,我們看到error窗口,但是達(dá)到5次報錯后,進(jìn)入熔斷后降級。
熱點(diǎn)規(guī)則-熱點(diǎn)key限流
源碼:com.alibaba.csp.sentinel.slots.block.BlockException
?自定義異常信息
?熱點(diǎn)規(guī)則
?方法testHotKey里面的第一個參數(shù)(參數(shù)索引設(shè)置為0)主要QPS超過每秒1次,馬上降級處理,會調(diào)用deal_testHotKey。
也就是說:
訪問http://localhost:8080/testHotKey?p1=abc? 熱點(diǎn)規(guī)則生效
訪問http://localhost:8080/testHotKey?p1=abc&p2=123? 熱點(diǎn)規(guī)則生效
訪問http://localhost:8080/testHotKey?p2=abc? 熱點(diǎn)規(guī)則不生效
參數(shù)例外項
?
?
場景:
1.普通場景:超過1秒鐘一個后,達(dá)到閾值1后馬上限流。
2.特例:我們希望p1參數(shù)當(dāng)它是某個特殊值時,它的限流值和平時不一樣(加入當(dāng)p1的值等于5時,它的閾值可以達(dá)到200)。
方法中報錯,該報異常報異常,它只處理自己的異常:
?@SentinelResource處理的是Sentinel控制臺配置的違規(guī)情況,有blockHandler方法配置的兜底處理。
RuntimeException:int age = 10/0,這個是java運(yùn)行時報出的運(yùn)行時異常,@SentinelResource不管。
總結(jié):@SentinelResource主管配置出錯,運(yùn)行出錯走正常的異常處理邏輯。
系統(tǒng)規(guī)則(總控-所有的uri的控制)
?系統(tǒng)保護(hù)規(guī)則是從應(yīng)用級別的入口流量進(jìn)行控制,從單臺及其的load、CPU使用率、平均RT、入口QPS和并發(fā)線程數(shù)等幾個維度監(jiān)控應(yīng)用指標(biāo),讓系統(tǒng)盡可能跑在最大吞吐量的同時保證系統(tǒng)整體的穩(wěn)定性。
系統(tǒng)保護(hù)規(guī)則是應(yīng)用整體維度的,而不是資源維度的,并且僅對入口流量生效。入口流量指的是進(jìn)入應(yīng)用的流量(EntryType.IN),比如Web服務(wù)或Dubbo服務(wù)端接收的請求,都屬于入口流量。
系統(tǒng)規(guī)則支持以下的模式:
· Load自適應(yīng)(僅對Linux/Unix-like機(jī)器生效):系統(tǒng)的load1作為啟發(fā)指標(biāo),進(jìn)行自適應(yīng)系統(tǒng)保護(hù)。當(dāng)系統(tǒng)load1超過設(shè)定的啟發(fā)值,且系統(tǒng)當(dāng)前的并發(fā)線程數(shù)超過估算的系統(tǒng)容量時才會觸發(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使用率超過閾值即觸發(fā)系統(tǒng)保護(hù)(取值范圍0.0 - 1.0),比較靈敏。
· 平均RT:當(dāng)單臺機(jī)器上所有入口流量的平均RT達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù),單位是毫秒。
· 并發(fā)線程數(shù):當(dāng)單臺機(jī)器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
· 入口QPS:當(dāng)單臺機(jī)器上所有入口流量的QPS達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
自定義兜底方法(錯誤信息)
@GetMapping("/rateLimit/customerBlockHandler") @SentinelResource(value = "customerBlockHandler",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handlerException2") public CommonResult customerBlockHandler() {return new CommonResult(200,"按客戶自定義",new Payment(2020L,"serial003")); }public class CustomerBlockHandler {public static CommonResult handlerException(BlockException exception){return new CommonResult(4444,"按客戶自定義,global handlerException----1");}public static CommonResult handlerException2(BlockException exception){return new CommonResult(4444,"按客戶自定義,global handlerException----2");} }?@SentinelResource注解
注意:注解方式埋點(diǎn)不支持private方法。
?@SentinelResource用于定義資源,并提供可選的異常處理和fallback配置項。?@SentinelResource注解包含以下屬性:
· value:資源名稱、必需項(不能為空)。
· entryType:entry類型,可選項(默認(rèn)為EntryType.OUT)。
· blockHandler / blockHandlerClass:blockHandler對應(yīng)處理BlockException的函數(shù)名稱,可選項。blockHandler函數(shù)訪問范圍需要是public,返回類型需要與原方法相匹配,參數(shù)類型需要和原方法相匹配并且最后加一個額外的參數(shù),類型為BlockException。blockHandler函數(shù)默認(rèn)需要和原方法在同一個類中。若希望使用其他類的函數(shù),則可以指定blockHandlerClass為對應(yīng)的類的Class對象,注意對應(yīng)的函數(shù)必需為static函數(shù),否則無法解析。
· fallback:fallback函數(shù)名稱,可選項,用于在拋出異常的時候提供fallback處理邏輯。fallback函數(shù)可以針對所有類型的異常(除了exceptionToIgnore里面排除掉的異常類型)進(jìn)行處理。fallback函數(shù)簽名和位置要求:
① 返回值類型必須與原函數(shù)返回值類型一致。
② 方法參數(shù)列表需要和原函數(shù)一致,或者可以額外多一個Throwable類型的參數(shù)用于接收對應(yīng)的異常。
③ 方法參數(shù)列表需要和原方法在同一個類中。若希望使用其他類的函數(shù),則可以指定fallbackClass為對應(yīng)的類的Class對象,注意對應(yīng)的函數(shù)必需為static函數(shù),否則無法解析。
· defaultFallback(since1.6.0):默認(rèn)的fallback函數(shù)名稱,可選項,通常用于通用的fallback邏輯(即可以用于很多服務(wù)或方法)。默認(rèn)fallback函數(shù)可以針對所有類型的異常(除了exceptionsToIgnore里面排除掉的異常類型)進(jìn)行處理。若同時配置了fallback和defaultFallback,則只有fallback會生效。defaultFallback函數(shù)簽名要求:
① 返回值類型必須與原函數(shù)返回值類型一致。
② 方法參數(shù)列表需要為空,或者可以額外多一個Throwable類型的參數(shù)用于接收對應(yīng)的異常。
ribbon、nacos、sentinel一起使用
@RequestMapping("/consumer/fallback/{id}") //@SentinelResource(value = "fallback") //沒有配置 //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只負(fù)責(zé)業(yè)務(wù)異常 //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只負(fù)責(zé)sentinel控制臺配置違規(guī) //若blockHandler和fallback都進(jìn)行了配置,則被限流降級而拋出BlockException時只會進(jìn)入blockHandler處理邏輯。 @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",exceptionsToIgnore = {IllegalArgumentException.class}) public CommonResult<Payment> fallback(@PathVariable Long id) {CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);if (id == 4) {throw new IllegalArgumentException ("IllegalArgumentException,非法參數(shù)異常....");}else if (result.getData() == null) {throw new NullPointerException ("NullPointerException,該ID沒有對應(yīng)記錄,空指針異常");}return result; } //本例是fallback public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底異常handlerFallback,exception內(nèi)容 "+e.getMessage(),payment); } //本例是blockHandler public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {Payment payment = new Payment(id,"null");return new CommonResult<>(445,"blockHandler-sentinel限流,無此流水: blockException "+blockException.getMessage(),payment); }異常忽略
?feign
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class) public interface PaymentService {@GetMapping(value = "/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id); }@Component public class PaymentFallbackService implements PaymentService {@Overridepublic CommonResult<Payment> paymentSQL(Long id){return new CommonResult<>(44444,"服務(wù)降級返回,---PaymentFallbackService",new Payment(id,"errorSerial"));} }@GetMapping(value = "/consumer/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {return paymentService.paymentSQL(id); } # 激活Sentinel對Feign的支持 feign:sentinel:enabled: truesentinel、Hystrix、resilience4j的對比
| sentinel | Hystrix | resilience4j | |
| 隔離策略 | 信號量隔離(并發(fā)線程數(shù)限流) | 線程池隔離/信號量隔離 | 信號量隔離 |
| 熔斷降級策略 | 基于響應(yīng)時間、異常比率、異常數(shù) | 基于異常比率 | 基于異常比率、響應(yīng)時間 |
| 實時統(tǒng)計實現(xiàn) | 滑動窗口(LeapArray) | 滑動窗口(基于RxJava) | Ring Bit Buffer |
| 動態(tài)規(guī)則配置 | 支持多種數(shù)據(jù)源 | 支持多種數(shù)據(jù)源 | 有限支持 |
| 擴(kuò)展性 | 多個擴(kuò)展點(diǎn) | 插件的形式 | 接口的形式 |
| 基于注解的支持 | 支持 | 支持 | 支持 |
| 限流 | 基于QPS,支持基于調(diào)用關(guān)系的限流 | 有限的支持 | Rate Limiter |
持久化
服務(wù)重啟后,sentinel在管理端的配置就會重置,所以需要持久化。
使用nacos做sentinel的持久化
<!--SpringCloud ailibaba sentinel-datasource-nacos 后續(xù)做持久化用到--> <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId> </dependency> spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服務(wù)注冊中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard地址port: 8719datasource:ds1:nacos:server-addr: localhost:8848dataId: cloudalibaba-sentinel-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flow?
resource:資源名稱。
limitApp:來源應(yīng)用。
grade:閾值類型,0表示線程數(shù),1表示QPS。
count:單機(jī)閾值。
strategy:流控模式,0表示直接,1表示關(guān)聯(lián),2表示鏈路。
controlBehavior:流控效果,0表示快速失敗,1表示W(wǎng)arm Up,2表示排隊等待。
clusterMode:是否集群。
?
?
總結(jié)
以上是生活随笔為你收集整理的详解sentinel:分布式系统的流量防卫兵的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot启动流程,手把手打断
- 下一篇: Pygame初始-模仿windows待机