Sentinel 是啥?
分佈式系統的流量防衛兵
引用一下之前我畫的圖:
流量防衛兵
它具備了哪些能力?
Sentinel 的生態環境
隨着 Alibaba
的 Java 生態建設,包括 Spring Cloud Alibaba
,Rocket
,Nacos
等多項開源技術的貢獻,目前Sentinel
對分佈式的各種應用場景都有了良好的支持和適配,這也是爲什麼我們選擇 Sentinel
學習的原因之一(學習成本低,應用場景多)
Sentinel 核心概念
1、資源
資源
是 Sentinel
中的核心概念之一。最常用的資源是我們代碼中的 Java 方法
,一段代碼
,或者一個接口
。
Java方法:
@SentinelResource("HelloWorld")
public void helloWorld() {
// 資源中的邏輯
System.out.println("hello world");
}
一段代碼:
// 1.5.0 版本開始可以直接利用 try-with-resources 特性,自動 exit entry
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保護的邏輯
System.out.println("hello world");
} catch (BlockException ex) {
// 處理被流控的邏輯
System.out.println("blocked!");
}
一個接口:
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "test";
}
}
配合控制檯使用:
2、規則
Sentinel
中的規則
提供給用戶,針對不同的場景而制定不同的保護動作,規則的類型包括:
流量
控制規則熔斷
降級規則系統保護
規則來源訪問控制規則
熱點參數規則
本文主要會講解 流量
,熔斷
和系統保護
這三個規則。
定義規則:
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//綁定資源
rule.setResource("HelloWorld");
//限流閾值類型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//數量級別
rule.setCount(20);
//添加到本地內存
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
限流規則
重要屬性說明:
Sentinel 限流
1、單機限流
1.1、引入依賴
在上一篇文章中,有提到過 RateLimiter
實現的單機限流, 這裏介紹一下,使用 Sentinel
實現的單機限流
//項目中引入 sentinel-core 依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.1</version>
</dependency>
1.2、定義限流規則
定義保護規則:
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//綁定資源
rule.setResource("HelloWorld");
//限流閾值類型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//數量級別
rule.setCount(20);
//添加到本地內存
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
1.3、定義限流資源
根據上面描述的 資源
劃分, 我們這裏主要將 代碼塊
定義爲資源。
public static void main(String[] args) {
// 配置規則.
initFlowRules();
while (true) {
// 1.5.0 版本開始可以直接利用 try-with-resources 特性,自動 exit entry
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保護的邏輯
System.out.println("hello world");
} catch (BlockException ex) {
// 處理被流控的邏輯
System.out.println("blocked!");
}
}
}
1.4、運行結果
Demo 運行之後,我們可以在日誌
~/logs/csp/${appName}-metrics.log.xxx
裏看到下面的輸出:
➜ csp cat com-jaycekon-sentinel-demo-FlowRuleDemo-metrics.log.2021-07-03
|--timestamp-|------date time----|-resource-|p |block|s |e|rt
1625294582000|2021-07-03 14:43:02|HelloWorld|20|1720|20|0|2|0|0|0
1625294583000|2021-07-03 14:43:03|HelloWorld|20|5072|20|0|0|0|0|0
1625294584000|2021-07-03 14:43:04|HelloWorld|20|6925|20|0|0|0|0|0
p
代表通過的請求block
代表被阻止的請求s
代表成功執行完成的請求個數e
代表用戶自定義的異常rt
代表平均響應時長
Sentinel
的單機限流 ,和 RateLimiter
有什麼區別呢?
附錄:《Sentinel - 滑動窗口實現原理》
2、控制檯限流
2.1、客戶端接入控制檯
超詳細文檔,參考:《Sentinel - 控制檯》
Sentinel 提供一個輕量級的開源控制檯,它提供機器發現以及健康情況管理、監控(單機和集羣),規則管理和推送的功能。
下載Jar 包(21M)
,或者下載源碼(4M)
後自行進行編譯(不建議,編譯花的時間比直接下載jar包還要久)
https://github.com/alibaba/Sentinel/releases
編譯後,啓動命令
java -Dserver.port=8000 -Dcsp.sentinel.dashboard.server=localhost:8000 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
進入控制檯
2.2、引入依賴
客戶端需要引入 Transport
模塊來與 Sentinel
控制檯進行通信。您可以通過 pom.xml
引入 JAR 包
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.1</version>
</dependency>
//重要的依賴,還是提前先寫上吧,避免小夥伴找不到了
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>1.8.1</version>
</dependency>
然後!!!煩了我一下午的地方來了!!在官方文檔中,指出了需要引入對應的依賴配置
, 好傢伙,那麼重要的話,你如此輕描淡寫,腦殼疼啊!!!
對應的適配依賴有
雲原生微服務體系
Web 適配
RPC 適配
HTTP client 適配
Reactive 適配
Reactive 適配
Apache RocketMQ
好傢伙,基本上所有業務場景都覆蓋到了!由於我的Demo
項目是基於 SpringBoot
,然後想看看 雲原生微服務體系下的視頻,好傢伙,要用 SpringCloud
, 想要了解的,可以參考: 《Spring-Cloud-Sentinel》
2.3、定義資源
@SpringBootApplication
@Configuration
@RestController
public class SpringBootSentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSentinelApplication.class, args);
}
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
@RequestMapping("/index")
public String index(){
return "hello index";
}
}
在概述中,我們有提到過,需要被保護的資源,可以是 一個代碼塊
,一個方法
或者一個接口
。這裏通過 Filter
的 方式,將所有請求都定義爲資源 (/*
), 那麼我們在請求的過程就會變成這樣子:
2.4 運行結果
添加啓動參數
-Dserver.port=8088 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=jaycekon-sentinel
參數說明:
server.port
: 服務啓動端口csp.sentinel.dashboard.server
: 狀態上報機器ip:端口project.name
: 監控項目名稱
運行結果:
2.5 限流配置
流控效果
1、快速失敗:直接失敗
2、Warm Up:預熱模式,根據codeFactory的值(默認3),從閾值/codeFactory,經過預熱時長,才達到設置的QPS閾值。比如設置QPS爲90,設置預熱爲10秒,則最初的閾值爲90/3=30,經過10秒後才達到90。
3、排隊等待:比如設置閾值爲10,超時時間爲500毫秒,當第11個請求到的時候,不會直接報錯,而是等待500毫秒,如果之後閾值還是超過10,則纔會被限流。
運行結果:
3、集羣限流
講了那麼多,終於要到核心的 集羣限流
方案了, 在秒殺系統
設計中,我們談到很多場景都是以單機作爲具體案例進行分析,如果我們的系統要擴容,那麼如何做好限流方案
。假設集羣中有 10 臺機器,我們給每臺機器設置單機限流閾值爲10 QPS
,理想情況下整個集羣的限流閾值就爲100 QPS
。不過實際情況下流量到每臺機器可能會不均勻
,會導致總量沒有到的情況下某些機器就開始限流。因此僅靠單機維度去限制的話會無法精確
地限制總體流量。而集羣流控
可以精確地控制整個集羣的調用總量,結合單機限流兜底
,可以更好地發揮流量控制的效果。
介紹一下集羣限流的核心角色:
Token Client
:集羣流控客戶端,用於向所屬 Token Server 通信請求 token。集羣限流服務端會返回給客戶端結果,決定是否限流。Token Server
:即集羣流控服務端,處理來自 Token Client 的請求,根據配置的集羣規則判斷是否應該發放 token(是否允許通過)。
在嵌入模式下的結構圖:
在獨立模式下的結構圖:
內嵌模式
,即 發Token 的操作,有其中某一個實例完成,其他 Client 通過向 Server 請求,獲取訪問許可。
獨立模式
,即作爲獨立的 token server 進程啓動,獨立部署,隔離性好,但是需要額外的部署操作。
3.1、阿里雲AHAS
在上述示例代碼中,使用了本地模式的 Demo
, 在集羣限流的場景,這裏用一下 阿里雲提供的 AHAS
服務。
控制檯地址:https://ahas.console.aliyun.com/index?ns=default®ion=public
引入依賴:
//sentinel ahas 依賴,包括了sentinel的使用依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>ahas-sentinel-client</artifactId>
<version>1.8.8</version>
</dependency>
這裏有個要注意的點, AHAS
的依賴,包含了 Sentinel
,所需要使用到的依賴,包括 sentinel-core
,sentinel-web-servlet
和sentinel-transport-simple-http
。
否則會出現 Spi
異常 , 如果對 Spi
不太瞭解,建議加羣提問,嘿嘿~
com.alibaba.csp.sentinel.spi.SpiLoaderException
3.2、開啓阿里雲AHAS 服務
這裏有官方的開通文檔,我就不贅述了,文檔地址
在應用防護這裏找到 Lincense
,然後添加啓動參數:
-Dserver.port=8092 -Dproject.name=jaycekon-sentinel -Dahas.license=d1e21b0c8f2e4d87b5ac460b118dc58d -Dcsp.sentinel.log.use.pid=true
由於我們要本地啓動多實例, 因此需要修改服務的多個端口:
java -Dserver.port=8090 -Dproject.name=jaycekon-sentinel -Dahas.license=d1e21b0c8f2e4d87b5ac460b118dc58d -Dcsp.sentinel.log.use.pid=true -jar sentinel-ahas-0.0.1-SNAPSHOT.jar
java -Dserver.port=8091 -Dproject.name=jaycekon-sentinel -Dahas.license=d1e21b0c8f2e4d87b5ac460b118dc58d -Dcsp.sentinel.log.use.pid=true -jar sentinel-ahas-0.0.1-SNAPSHOT.jar
java -Dserver.port=8092 -Dproject.name=jaycekon-sentinel -Dahas.license=d1e21b0c8f2e4d87b5ac460b118dc58d -Dcsp.sentinel.log.use.pid=true -jar sentinel-ahas-0.0.1-SNAPSHOT.jar
產生訪問流量後,可以在大盤看到機器的鏈接狀態:
http://localhost:8092/index
3.3、集羣流控規則配置
這裏有個兩個概念:
集羣閥值:指的是,我們集羣總體能通過的訪問量,可能存在分配不均的情況(能避免單機誤限)。
退化單機:當 Token Server 訪問超時,即無法從遠端獲取令牌時,回退到單機限流
測試限流, 只訪問 http://localhost:8092/index
通過手刷(手速過硬~),觸碰到限流的臨界值,然後整體限流跟我們預期一致。
退化單機
在集羣流控這裏,有個 Token 請求超時時間,Client
請求 Server
,然後返回數據結果。整個流程會有網絡請求的耗時,在上面的測試流程中,我將超時時間調大了,每次請求都能拿到Token, 通過修改請求超時時間,觸發退化 單機限流
。
運行結果:
3.4、Server 角色轉換
在內嵌模式下,通過 HTTP API的方式,將角色轉換爲 Server
或 client
http://<ip>:<port>/setClusterMode?mode=<xxx>
其中 mode 爲 0
代表 client,1
代表 server,-1
代表關閉。注意應用端需要引入集羣限流客戶端或服務端的相應依賴。
在獨立模式下,我們可以直接創建對應的 ClusterTokenServer
實例並在 main 函數中通過 start
方法啓動 Token Server。
Sentinel 熔斷
在秒殺系統
的案例中,一個完整的鏈路可能包含了 下訂單
,支付
和物流對接
等多個服務(實際上不止那麼少)。在一個完整的鏈路中,各個系統通過 rpc/http的形式進行交互,在下面的鏈路圖中,如果用戶選擇的 支付方式,存在延時過高
,服務不穩定
,或服務異常
等情況,會導致整個鏈路沒辦法完成。最終的結果就是,用戶明明搶到了,但是沒辦法支付,導致訂單丟失。
現代微服務架構都是分佈式
的,由非常多的服務
組成。不同服務之間相互調用,組成複雜的調用鏈路
。以上的問題在鏈路調用中會產生放大的效果。複雜鏈路上的某一環不穩定
,就可能會層層級聯
,最終導致整個鏈路
都不可用。因此我們需要對不穩定的弱依賴服務調用進行熔斷降級,暫時切斷不穩定調用,避免局部不穩定因素導致整體的雪崩
。熔斷降級
作爲保護自身的手段,通常在客戶端(調用端)
進行配置。
1、熔斷降級
添加測試代碼
@RequestMapping("/myError")
public String error(){
if (true){
throw new RuntimeException("sentinel run error");
}
return "error";
}
降級保護效果:
用戶通過訪問接口 /myError
, 出現一次異常後,在接下來的10秒
,都會走降級策略,直接返回。能夠很好的保護服務端避免異常過多,佔用機器資源。同時快速響應用戶請求。
2、熔斷策略
Sentinel 提供以下幾種熔斷策略:
慢調用比例 (
SLOW_REQUEST_RATIO
):選擇以慢調用比例作爲閾值,需要設置允許的慢調用 RT(即最大的響應時間),請求的響應時間大於該值則統計爲慢調用。當單位統計時長(statIntervalMs
)內請求數目大於設置的最小請求數目,並且慢調用的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求響應時間小於設置的慢調用 RT 則結束熔斷,若大於設置的慢調用 RT 則會再次被熔斷。異常比例 (
ERROR_RATIO
):當單位統計時長(statIntervalMs
)內請求數目大於設置的最小請求數目,並且異常的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。異常比率的閾值範圍是[0.0, 1.0]
,代表 0% - 100%。異常數 (
ERROR_COUNT
):當單位統計時長內的異常數目超過閾值之後會自動進行熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。
總結
本文主要詳細講解了一下 如何通過 Sentinel 去實際接觸 限流和熔斷,對於限流的底層實現,後續會有專門的源碼分析篇。對於熔斷,本文也沒有細說個究竟,下一篇文章會給大家帶來,熔斷是什麼,在系統中到底是怎麼實際使用,以及常見的熔斷策略。
項目源碼地址:https://github.com/jaycekon/SpringBoot
歡迎 Star 和 Fork
有道無術,術可成;有術無道,止於術
歡迎大家關注Java之道公衆號
好文章,我在看❤️
本文分享自微信公衆號 - Hollis(hollischuang)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。