概述
Sentinel
譯為“哨兵”,顧名思義,面對您后臺的大量服務(wù)/微服務(wù),前置一個哨兵,但面對大量請求時,讓后臺服務(wù)有序被調(diào)用,但某些服務(wù)的不可用時,采用服務(wù)熔斷降級等措施,讓系統(tǒng)仍能平穩(wěn)運行,不至于造成系統(tǒng)雪崩,典型應(yīng)用場景:
- MQ中消息在某些時間段(比如行情交易的高峰期,秒殺期等)消息并發(fā)量非常大時,通過
Sentinel
起到“削峰填谷”的作用; - 某個業(yè)務(wù)服務(wù)非常復(fù)雜,需要調(diào)用大量微服務(wù),其中某服務(wù)不可用時,不影響整體業(yè)務(wù)運行,如提交某個訂單,需要調(diào)用諸如驗證庫存,驗證優(yōu)惠金額,支付,驗證手機號等,其中驗證手機號服務(wù)不可用時,采用降級的方式讓其通過,不影響整個提交訂單的業(yè)務(wù);
- 上述訂單業(yè)務(wù)提交時,依賴的下游應(yīng)用控制線程數(shù),請求上下文超過閾值時,新的請求立即拒絕,即針對流控,可基于QPS或線程數(shù)在某些業(yè)務(wù)場景下,都會有用,如下就是一個qps瞬時拉大時,通過流量緩慢增加,避免系統(tǒng)被壓垮的情況:
隨著微服務(wù)的流行,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來越重要。Sentinel
以流量為切入點,從流量控制、熔斷降級、系統(tǒng)負載保護等多個維度保護服務(wù)的穩(wěn)定性。
Sentinel 具有以下特征:
豐富的應(yīng)用場景:
Sentinel
承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)、消息削峰填谷、實時熔斷下游不可用應(yīng)用等。完備的實時監(jiān)控:
Sentinel
同時提供實時的監(jiān)控功能。您可以在控制臺中看到接入應(yīng)用的單臺機器秒級數(shù)據(jù),甚至 500 臺以下規(guī)模的集群的匯總運行情況。廣泛的開源生態(tài):
Sentinel
提供開箱即用的與其它開源框架/庫的整合模塊,例如與Spring Cloud
、Dubbo
、gRPC
的整合。您只需要引入相應(yīng)的依賴并進行簡單的配置即可快速地接入Sentinel
。完善的
SPI
擴展點:Sentinel
提供簡單易用、完善的SPI
擴展點。您可以通過實現(xiàn)擴展點,快速的定制邏輯。例如定制規(guī)則管理、適配數(shù)據(jù)源等。
Sentinel
分為兩部分:
核心庫(Java 客戶端)不依賴任何框架/庫,能夠運行于所有 Java 運行時環(huán)境,同時對 Dubbo / Spring Cloud 等框架也有較好的支持。
控制臺(Dashboard)基于 Spring Boot 開發(fā),打包后可以直接運行,不需要額外的 Tomcat 等應(yīng)用容器,該模塊目前基于SpringBoot運行;
部署
在使用Sentinel
之前,我們首先需部署Sentinel Dashborad
,下載最新版的Sentinel Dashborad
,通過以下命令運行:
java -Dserver.port=8088 -jar sentinel-dashboard-1.3.0.jar
通過server.port
執(zhí)行程序運行端口號,通過http://localhost:8088
打開控制臺,如下:
至此部署完成,非常簡單!
項目中使用Sentinel
Sentinel針對各個主流框架都提供了適配(包括Servlet,Dubbo,SpringBoot/SpringCloud,gRPC,RocketMQ等),本文以SpringBoot2
舉例(通過筆者測試發(fā)現(xiàn),SpringBoot 1.x支持不好,自定義流控規(guī)則不可用),首先我們需要在SpringBoot2
的配置文件中指定Sentinel連接的控制臺地址和項目名,即application.yml
文件,如下:
project:
name: 在控制臺顯示的項目名
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.1.154:8088
在項目中加入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>0.2.0.RELEASE</version>
</dependency>
啟動SpringBoot2
后,可以在簇點鏈路頁面看到項目的各個服務(wù)(首次訪問服務(wù)時會被出現(xiàn)在列表中,這些服務(wù)在Sentinel中被稱為資源),接著,你就可以針對這些資源進行流控,降級,熱點,授權(quán)等操作。
流量控制與規(guī)則擴展
在控制臺可通過流控規(guī)則菜單定義某資源的流控規(guī)則,不過這個定義只在內(nèi)存中,但控制臺重啟后,隨之消失,所以我們一般在項目中通過配置文件來定義流控規(guī)則,編寫一個流控數(shù)據(jù)源,如下:
package com.sumscope.study.springboot2.service;
import java.net.URLDecoder;
import java.util.List;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.Rule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
public class FileDataSourceInit implements InitFunc{
private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(source,
new TypeReference<List<FlowRule>>() {});
private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(source,
new TypeReference<List<DegradeRule>>() {});
private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(source,
new TypeReference<List<SystemRule>>() {});
@Override
public void init() throws Exception {
ClassLoader classLoader = getClass().getClassLoader();
String flowRulePath = URLDecoder.decode(classLoader.getResource("FlowRule.json").getFile(), "UTF-8");
String degradeRulePath = URLDecoder.decode(classLoader.getResource("DegradeRule.json").getFile(), "UTF-8");
String systemRulePath = URLDecoder.decode(classLoader.getResource("SystemRule.json").getFile(), "UTF-8");
// Data source for FlowRule
FileRefreshableDataSource<List<FlowRule>> flowRuleDataSource = new FileRefreshableDataSource<>(
flowRulePath, flowRuleListParser);
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
// Data source for DegradeRule
FileRefreshableDataSource<List<DegradeRule>> degradeRuleDataSource
= new FileRefreshableDataSource<>(
degradeRulePath, degradeRuleListParser);
DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());
// Data source for SystemRule
FileRefreshableDataSource<List<SystemRule>> systemRuleDataSource
= new FileRefreshableDataSource<>(
systemRulePath, systemRuleListParser);
SystemRuleManager.register2Property(systemRuleDataSource.getProperty());
}
}
然后在項目的resources
下新增目錄META-INF\services\com.alibaba.csp.sentinel.init.InitFunc
,并填寫以上類的完成類名如com.sumscope.study.springboot2.service.FileDataSourceInit
即可,這樣,在您的classpath目錄下通過DegradeRule.json
,FlowRule.json
,SystemRule.json
分別來定義降級規(guī)則,流控規(guī)則和系統(tǒng)規(guī)則,比如我們定義一個流控規(guī)則,讓test資源QPS為1,即1秒鐘最多調(diào)用1次,如下:
[
{
"resource": "test",
"controlBehavior": 0,
"count": 1,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]
在Java代碼中,通過
@SentinelResource("test")
來定義服務(wù)對應(yīng)的資源名,如果不指數(shù),URI即為資源名。
熔斷降級
降級規(guī)則定義如下:
可基于RT(平均響應(yīng)時間)或異常比例兩種方式來定義,其中RT單位為毫秒。
指定RT時,當資源的平均響應(yīng)時間超過閾值(DegradeRule 中的 count,以 ms 為單位)之后,資源進入準降級狀態(tài)。接下來如果持續(xù)進入 5 個請求,它們的 RT 都持續(xù)超過這個閾值,那么在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 為單位)之內(nèi),對這個方法的調(diào)用都會自動地返回。
指定異常時,當資源的每秒異常總數(shù)占通過總數(shù)的比值超過閾值(DegradeRule 中的 count)之后,資源進入降級狀態(tài),即在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 為單位)之內(nèi),對這個方法的調(diào)用都會自動地返回。
熱點參數(shù)限流
也可對經(jīng)常訪問的數(shù)據(jù)進行限流,如某個商品或某個用戶等,如下圖:
Sentinel
利用 LRU
策略,結(jié)合底層的滑動窗口機制來實現(xiàn)熱點參數(shù)統(tǒng)計。LRU 策略可以統(tǒng)計單位時間內(nèi),最近最常訪問的熱點參數(shù),而滑動窗口機制可以幫助統(tǒng)計每個參數(shù)的 QPS,熱點參數(shù)限流目前只支持QPS模式。
黑白名單
可通過定義策略(黑名單或白名單)限定資源的調(diào)用方是否讓其通過,以下是代碼定義白名單規(guī)則:
AuthorityRule rule = new AuthorityRule();
rule.setResource("test");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("appA,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
實時監(jiān)控
在控制臺我們可以實時看到每個資源的qps情況如下圖:
注:其中
p_qps
是每秒通過的請求數(shù),b_qps
是每秒拒絕的請求數(shù)