Sentinel是面向分布式服務(wù)架構(gòu)的輕量級流量控制組件,主要以流量為切入點(diǎn),從限流、流量整形、服務(wù)降級、系統(tǒng)負(fù)載保護(hù)等多個維度來幫助我們保障微服務(wù)的穩(wěn)定性。
穩(wěn)定是系統(tǒng)的基礎(chǔ)能力,穩(wěn)定性差的系統(tǒng)會出現(xiàn)服務(wù)超時或服務(wù)不可用,給用戶帶來不好的體驗,也會對業(yè)務(wù)造成不良影響。
Sentinel的特性
Sentinel有如下特性:
- 應(yīng)用場景豐富:幾乎涵蓋所有的應(yīng)用場景,例如秒殺、消息削峰填谷、集群流量控制等。
- 實時監(jiān)控:Sentinel提供了實時監(jiān)控功能,開發(fā)者可以在控制臺中看到接入應(yīng)用的單臺機(jī)器秒級數(shù)據(jù),甚至500臺以下規(guī)模的集群匯總運(yùn)行情況。
- 支持開源生態(tài):Sentinel提供開箱即用的與其它開源框架的整合,例如Spring Cloud、Dubbo、gRPC的整合。開發(fā)者只需要引入相應(yīng)的依賴并進(jìn)行簡單的配置即可快速接入Sentinel。
- 支持SPI擴(kuò)展點(diǎn):Sentinel提供SPI擴(kuò)展點(diǎn)支持,開發(fā)者可以通過擴(kuò)展點(diǎn)來定制化限流規(guī)則,動態(tài)數(shù)據(jù)源適配等需求。
Sentinel分為兩個部分:
- 核心庫(Java客戶端):不依賴任何框架,能夠運(yùn)行于所有Java運(yùn)行時環(huán)境,同時對Dubbo、Spring Cloud等框架也有較好的支持。
- 控制臺(Dashboard):基于Spring Boot開發(fā),打包后可以直接運(yùn)行,不需要額外的Tomcat等應(yīng)用容器。
Sentinel的部署
Sentinel提供一個輕量級的開源控制臺,它支持機(jī)器發(fā)現(xiàn)以及健康情況管理、監(jiān)控、規(guī)則管理和推送等功能。
Sentinel Dashboard的安裝步驟如下:
- 在GitHub中Sentinel的源碼倉庫中下載源碼或者下載已經(jīng)構(gòu)建好的jar
- 通過以下命令啟動控制臺
java -Dserver.port=7777 -Dcsp.sentinel.dashboard.server=localhost:7777 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
默認(rèn)登陸的用戶名和密碼都是sentinel
Sentinel的基本應(yīng)用
Sentinel實現(xiàn)限流
使用Sentinel的核心庫實現(xiàn)限流,主要分以下幾個步驟:
- 定義資源
- 定義限流規(guī)則
- 檢驗規(guī)則是否生效
資源就是通過限流保護(hù)的基本元素,比如一個方法。
首先需要引入Sentinel的核心庫:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.1</version>
</dependency>
然后定義一個普通的業(yè)務(wù)方法:
private static void testSentinel() {
try (Entry entry = SphU.entry("testSentinel")) {
System.out.println("Test Sentinel " + System.currentTimeMillis());
} catch (BlockException e) {
//
}
}
在testSentinel方法中,通過使用Sentinel中的SphU.entry("testSentinel")定義一個資源來實現(xiàn)流控的邏輯,它表示當(dāng)請求進(jìn)入testSentinel方法時,需要進(jìn)行限流判斷,如果拋出BlockException異常, 則表示觸發(fā)了限流。
接著我們要針對這個需要保護(hù)的資源定義限流規(guī)則:
private static void intiFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("testSentinel");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
對于資源testSentinel,通過initFlowRules方法設(shè)置限流規(guī)則,其中的參數(shù)含義如下:
- Grade: 限流閾值類型,QPS模式或者并發(fā)線程數(shù)模式
- count: 限流閾值
- resource: 設(shè)置需要保護(hù)的資源,這個資源的名稱必須與SphU.entry中使用的名稱保持一致
所以,上面代碼的意思是對于testSentinel這個方法,每秒鐘最多允許通過20個請求。
最后,通過main方法進(jìn)行測試:
public static void main(String[] args) {
intiFlowRules();
while (true) {
testSentinel();
}
}
運(yùn)行main方法之后,可以在{包名-類名}-metrics.log.date文件中看到如下日志:
1588859287000|2020-05-07 21:48:07|testSentinel|20|95214|20|0|0|0|0|0
1588859288000|2020-05-07 21:48:08|testSentinel|20|523818|20|0|0|0|0|0
1588859289000|2020-05-07 21:48:09|testSentinel|20|659792|20|0|0|0|0|0
上述日志對應(yīng)字段的含義如下:
timestamp|yyyy-MM-dd HH:mm:ss|resource|passQps|blockQps|successQps|exceptionQps|rt|occupiedPassQps|concurrency|classification
passQps: 代表通過的請求
blockQps: 代表被阻止的請求
successQps: 代表成功執(zhí)行完成的請求個數(shù)
exceptionQps: 代表用戶自定義的異常
rt: 代表平均響應(yīng)時長
occupiedPassQps: 代表優(yōu)先通過的請求
concurrency: 代表并發(fā)量
classification: 代表資源類型
從日志中可以看出,這個程序每秒穩(wěn)定輸出20次,和規(guī)則中預(yù)先設(shè)定的閾值是一樣的,而每秒被拒絕的請求高達(dá)60多萬次。
資源的定義方式
在上面的例子中,我們通過拋出異常的方式來定義一個資源,也就是當(dāng)資源被限流后,會拋出一個BlockException異常,這時我們需要捕獲該異常進(jìn)行限流后的處理:
private static void testSentinel() {
try (Entry entry = SphU.entry("resourceName")) {
// ......
} catch (BlockException e) {
// 被限流
}
}
其中,resourceName可以定義方法名稱、接口名稱或者其它的唯一標(biāo)識。
除此之外,還可以通過返回布爾值的方式來定義資源:
if (SphO.entry("resourceName")) {
try {
// 被保護(hù)的業(yè)務(wù)邏輯
} finally {
SphO.exit();
}
} else {
// 資源訪問被限制
}
在這種方式中,需要注意資源使用完成之后需要調(diào)用SphO.exit(),否則會導(dǎo)致調(diào)用鏈記錄異常,拋出ErrorEntryFreeException異常。
Sentinel還可以使用@SentinelResource支持注解的方式來定義資源:
@SentinelResource(value = "findUserName", blockHandler = "blockHandlerForUserName")
public String findUserName(String id) {
return "";
}
public String blockHandlerForUserName(String id, BlockException e) {
// 被限流后的處理方法
return "";
}
需要注意的是,blockHandler所配置的值blockHandlerForUserName會在觸發(fā)限流之后調(diào)用,這個方法的定義必須和原始方法findUserName的返回值、參數(shù)保持一直,而且要增加BlockException參數(shù)。
Sentinel實現(xiàn)服務(wù)熔斷
Sentinel實現(xiàn)服務(wù)熔斷操作的配置和限流類似,不同之處在于限流采用的是FlowRule,而熔斷中采用的是DegradeRule:
private static void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource("KEY");
degradeRule.setCount(10);
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
degradeRule.setTimeWindow(10);
degradeRule.setMinRequestAmount(5);
degradeRule.setRtSlowRequestAmount(5);
rules.add(degradeRule);
}
其中幾個屬性的說明如下:
- grade: 熔斷策略,支持秒級RT、秒級異常比例、分鐘級異常數(shù)。默認(rèn)是秒級RT
- timeWindow: 熔斷降級的時間窗口,單位為s,也就是觸發(fā)熔斷降級之后多長時間內(nèi)自動熔斷。
- rtSlowRequestAmount: 在RT模式下,1s內(nèi)持續(xù)多少個請求的平均RT超出閾值后觸發(fā)熔斷,默認(rèn)值是5.
- minRequestAmount: 觸發(fā)的異常熔斷最小請求數(shù),請求數(shù)小于該值時即使異常比例超出閾值也不會觸發(fā)熔斷,默認(rèn)值是5。
Sentinel提供三種熔斷策略,對于不同策略,參數(shù)的含義也不相同。
- 平均響應(yīng)時間(RuleConstant.DEGRADE_GRADE_RT): 如果1s內(nèi)持續(xù)進(jìn)入5個請求,對應(yīng)的平均響應(yīng)時間都超過了閾值(count,單位為ms),那么在接下來的時間窗口(timeWindow,單位為s)內(nèi),對這個方法的調(diào)用都會自動熔斷,拋出DegradeException。
- 異常比例(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO):如果每秒資源數(shù)大于等于minRequestAmount(默認(rèn)值為5),并且每秒的異??倲?shù)占總通過量的比例超過閾值count(count的取值范圍是[0.0,1.0],代表0% ~ 100%),則資源將進(jìn)入降級狀態(tài)。同樣,在接下來的timeWindow之內(nèi),對于這個方法的調(diào)用都會自動觸發(fā)熔斷。
- 異常數(shù)(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT):當(dāng)資源最近一分鐘的異常數(shù)目超過閾值之后,會觸發(fā)熔斷。需要注意的是,如果timeWindow小于60s,則結(jié)束熔斷狀態(tài)后仍然可能再進(jìn)入熔斷狀態(tài)。
到此,我們對Sentinel已經(jīng)有了基本的了解。