文章目錄
-
-
- Sentinel五大規則學習
-
- 一、流控規則
- 二、降級規則
- 三、熱點規則
- 四、系統規則
- 五、授權規則
-
Sentinel五大規則學習
本次示範以sentinel 1.8.0版本為例,講解sentinel中流控、降級、熱點、系統、授權五大規則。
一、流控規則
流量控制,其原理是監控應用流量的QPS(每秒查詢率) 或并發線程數等名額,當達到指定的門檻值時對流量進行控制,以避免被瞬時的流量高峰沖垮,進而保障應用的高可用性。資源名:
接口路徑名
針對來源:指定對哪個微服務進行限流,預設指default,意思是不區分來源,全部限制
門檻值類型和單機門檻值:- QPS(每秒請求數量):: 當調用該接口的QPS達到門檻值的時候,進行限流
- 線程數:當調用該接口的線程數達到門檻值的時候,進行限流
暫不需要叢集
流控模式:
- 直接:直接流控模式是最簡單的模式,當指定的接口達到限流條件時開啟限流。
- 關聯:當指定接口關聯的接口達到限流條件時,對指定接口開啟限流。
- 鍊路:A、B接口都在調用C接口,A、B接口誰調用C達到限流條件就限流誰。
- 快速失敗(預設): 直接失敗,抛出異常,不做任何額外的處理,是最簡單的效果。
- Warm Up: 它從開始門檻值到最大QPS門檻值會有一個緩沖階段,一開始的門檻值是最大QPS門檻值的 1/3,然後慢慢增長,直到最大門檻值,适用于将突然增大的流量轉換為緩步增長的場景。
- 排隊等待: 讓請求以均勻的速度通過,單機門檻值為每秒通過數量,其餘的排隊等待; 它還會讓設定一個逾時時間,當請求超過逾時間時間還未處理,則會被丢棄。
主要示範三種流控模式,示範之前工作:
導入sentinel依賴:
<!-- SpringBoot 整合 nacos-sentinel 持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.0</version>
</dependency>
配置檔案:
server:
port: 8080
spring:
application:
name: mumber-producer #服務名程
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos預設端口位址為8848
enabled: true
sentinel:
transport:
dashboard: localhost:8718 #sentinel-dashboard 位址
eager: true
web-context-unify: false #關閉context整合
開啟sentinel控制台:
#jar指令啟動,自定義端口号(控制台本身是一個SpringBoot項目)
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
啟動mumber-producer服務注冊到控制台
1 直接模式
直接流控模式是最簡單的模式,當指定的接口達到限流條件時開啟限流。
- java代碼
@RestController
@Slf4j
public class MumberController1 {
@RequestMapping("/message1")
@SentinelResource(value = "message1", blockHandler = "insertUserBlockHandler")
public String message1() {
return "message1" + "";
}
}
/**
* blockHandler為自定義的提示方法
* 必須為public、傳回類型、參數類型與原方法一緻,額外加一個BlockException參數
* @return
*/
public String insertUserBlockHandler(BlockException e){
return "目前通路人數太多,請稍後重試!";
}
- 添加直接規則
- 通路接口測試
http://localhost:8080/message1
當每秒請求次數超過1次時,會進行限流。
2 關聯模式
當指定接口關聯的接口達到限流條件時,對指定接口開啟限流。(孩子惹事,家長負責)
- java代碼
@RestController
@Slf4j
public class MumberController1 {
@RequestMapping("/message1")
@SentinelResource(value = "message1", blockHandler = "insertUserBlockHandler")
public String message1() {
return "message1接口..." + "";
}
@RequestMapping("/message2")
@SentinelResource(value = "message2", blockHandler = "insertUserBlockHandler")
public String message2() {
return "message2接口...";
}
/**
* blockHandler為自定義的提示方法
* 必須為public、傳回類型、參數類型與原方法一緻,額外加一個BlockException參數
* @return
*/
public String insertUserBlockHandler(BlockException e){
return "目前通路人數太多,請稍後重試!";
}
}
- 添加關聯規則
- 通路測試
單獨通路message1、message2都不會達到限流,隻有頻繁通路message2(超過每秒1次時),再去通路message1,則message1會限流。
3 鍊路模式
當A、B接口兩個接口都在調用C接口,A、B接口誰調用C達到限流條件就限流誰,另一個接口不受影響。
- java代碼
@Service
@Slf4j
public class MumberServiceImpl {
@SentinelResource(value = "message")
public void message() {
log.info("--->message");
}
}
@RestController
@Slf4j
public class MumberController1 {
@Autowired
private MumberServiceImpl mumberServiceImpl;
@RequestMapping("/message1")
@SentinelResource(value = "message1", blockHandler = "insertUserBlockHandler")
public String message1() {
String message = mumberServiceImpl.message();
return "message1接口..." + "調用了" + message;
}
@RequestMapping("/message2")
@SentinelResource(value = "message2", blockHandler = "insertUserBlockHandler")
public String message2() {
String message = mumberServiceImpl.message();
return "message2接口..." + "調用了" + message;
}
/**
* blockHandler為自定義的提示方法
* 必須為public、傳回類型、參數類型與原方法一緻,額外加一個BlockException參數
* @return
*/
public String insertUserBlockHandler(BlockException e){
return "目前通路人數太多,請稍後重試!";
}
}
- 添加配置
server:
port: 8080
spring:
application:
name: mumber-producer #服務名程
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos預設端口位址為8848
enabled: true
sentinel:
transport:
dashboard: localhost:8718 #sentinel-dashboard 位址
eager: true
#添加這行配置
web-context-unify: false #關閉context整合
- 添加鍊路規則
理論上此時通過message2調用message接口不受影響,通過message1調用message接口則會限流。
但此處遇到一個bug,頻繁通路message1接口時控制台報錯:
百度許久依舊沒有找到答案,後期如果解決了我會及時更新這篇筆記以及貼出解決方案。
二、降級規則
資源名:接口路徑名
熔斷政策:- 慢調用比例(以前的版本也叫RT)
:選擇一個慢調用比例作為門檻值。
引入一個場景:A子產品調用B子產品
- 最大RT:Response Time 最大響應時間,即A調用B,B超過這個時間響應即為慢調用。
- 比例門檻值: 百分比,即通路接口慢調用達到百分之多少時,采用服務降級。
- 熔斷時長: 即觸發熔斷後,熔斷持續的時長,如A調用B,B響應太慢,是以A子產品采用降級處理,在一個時間段之内不會重複調用B。超過這個時間段過後再進行調用。
- 最小請求數: 熔斷觸發的最小請求數(避免1次2次偶爾響應時間慢就開啟降級處理,沒必要)。
- 異常比例:
- 比例門檻值: 百分比,即通路接口異常達到百分之多少時,采用服務降級。
- 熔斷時長: 即觸發熔斷後,熔斷持續的時長。
- 最小請求數: 熔斷觸發的最小請求數。
- 異常數:
- 異常數: 通路接口異常的次數達到多少觸發熔斷處理。
- 熔斷時長: 與上面一緻。
- 最小請求數: 與上面一緻。
現對三種降級政策開啟示範:
1 慢調用比例
- java代碼
@Service
@Slf4j
public class MumberServiceImpl {
public String message(int id) {
if (id % 2 == 0){
try {
Thread.sleep(500); //當傳遞過來的參數為偶數時,休眠500毫秒實作慢調用
} catch (Exception e){
e.printStackTrace();
}
return "slow message";
}
return "fast message";
}
}
@RestController
@Slf4j
public class MumberController1 {
@Autowired
private MumberServiceImpl mumberServiceImpl;
/**
* 降級政策---慢調用比例
*/
@GetMapping("/getMessage")
@SentinelResource(value = "getMessage", blockHandler = "insertUserBlockHandler")
public String getMessage(@RequestParam("id") int id){
log.info("--->:{}" + id);
return mumberServiceImpl.Message(id);
}
/**
* blockHandler為自定義的提示方法
* 必須為public、傳回類型、參數類型與原方法一緻,額外加一個BlockException參數
* @return
*/
public static String insertUserBlockHandler(int id,BlockException e){
return "目前通路人數太多,請稍後重試!" ;
}
}
- 新增熔斷降級
- 測試接口
localhost:8080/getMessage?id=2
攜帶偶數參數連續通路的接口占比50%時,會觸發降級處理:
localhost:8080/getMessage?id=1
攜帶奇數參數時不受限制:
2 異常比例
- java代碼
@Service
@Slf4j
public class MumberServiceImpl {
public String Message(int id) {
if (id % 2 == 0){
int i = 3 / 0; //人為構造一個異常
return "異常 message";
}
return "普通 message";
}
}
@RestController
@Slf4j
public class MumberController1 {
@Autowired
private MumberServiceImpl mumberServiceImpl;
/**
* 降級政策---異常比例
*/
@GetMapping("/getMessage")
@SentinelResource(value = "getMessage", blockHandler = "getMessageException")
public String getMessage(@RequestParam("id") int id){
log.info("--->:{}" + id);
return mumberServiceImpl.Message(id);
}
/**
* blockHandler為自定義的提示方法
* 必須為public、傳回類型、參數類型與原方法一緻,額外加一個BlockException參數
* @return
*/
public static String getMessageException(int id, BlockException e){
return "通路接口異常,請稍後重試!" ;
}
}
- 添加異常比例
- 測試
當攜帶參數為偶數時,會觸發異常,當通路這個異常接口占比達到50%時會降級處理。
localhost:8080/getMessage?id=2
攜帶偶數參數連續通路的接口占比50%時,會觸發降級處理:
localhost:8080/getMessage?id=1
攜帶奇數參數時不受限制:
3 異常數,與異常比例類似,此處不再示範,讀者一看就會。
三、熱點規則
在進行RESTful 設計的時候,一般都需要在 Action 的處理方法中進行請求參數的接收,于是這個時候可以針對于參數進行限流,這種規則就稱為熱點規則。
熱點參數流控規則是一種更細粒度的流控規則, 它允許将規則具體到參數上。
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-WNnRXjKp-1670498950020)(D:\筆記\sentinel\熱點規則.png)]
資源名:接口路徑名
參數索引:一般接口都有參數,從接口的第一個參數開始,以0為下标累加,每個參數都有一個下标。每個接口獨立計算。
單機門檻值:接口每秒的通路數。
統計視窗時長:限流接口的時效。
是否叢集:預設不叢集。
參數例外項:參數例外項就是可以達到更加細粒度的控制,比如我們的某個被設定為限流的參數,可以通過參數例外項設定參數具體等于某個特殊的值的時候,觸發不同的限流效果。例如有參數userId,普通設定為一個門檻值,當userId等于某個具體的數時,設定特殊門檻值。
代碼示範:
- java代碼
@RestController
@Slf4j
public class MumberController2 {
@RequestMapping("/user")
@SentinelResource(value = "user", blockHandler = "fail")
//此處age設定為Integer而非int,目的是Integer可以為null,而int不行
public String user(String name,Integer age) {
return "姓名:" +name + "---" + "年齡:" + age;
}
/**
* blockHandler為自定義的提示方法
* @return
*/
public String fail(String name,Integer age, BlockException e){
return "接口參數被限流了,請稍後重試!" ;
}
}
- 添加限流規則
- 通路測試
頻繁通路限流的接口:localhost:8080/user?name=zhnagsan
頻繁通路未被限流的接口:localhost:8080/user?age=20,無論怎麼通路,都沒有限流提示。
- 修改user接口第一個參數限流的參數例外項,當name為zhangsan時,限流門檻值修改為3(每秒允許通路3次,超過則限流)
再次頻繁通路限流的接口:localhost:8080/user?name=zhnagsan,發現限流速度比剛才慢了。
四、系統規則
系統保護規則是從應用級别的入口流量進行控制,從單台機器的總體 Load、RT、入口 QPS 、CPU使用率和線程數五個次元監控應用資料,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。系統保護規則是應用整體次元的,而不是資源次元的,并且僅對入口流量 (進入應用的流量) 生效。
- Load(僅對 Linux/Unix-like 機器生效): 當系統 load1 超過門檻值,且系統目前的并發線程數超過 系統容量時才會觸發系統保護。系統容量由系統的 maxQps * minRt 計算得出。設定參考值一般 是 CPU cores * 2.5。
- RT: 當單台機器上所有入口流量的平均 RT 達到門檻值即觸發系統保護,機關是毫秒。
- 線程數: 當單台機器上所有入口流量的并發線程數達到門檻值即觸發系統保護。
- 入口 QPS: 當單台機器上所有入口流量的 QPS 達到門檻值即觸發系統保護。
- CPU使用率: 當單台機器上所有入口流量的 CPU使用率達到門檻值即觸發系統保護
此處直接貼出alibaba sentinel官方的介紹,通俗易懂,就不示範實作了。
五、授權規則
授權規則可以對請求方來源做判斷和控制。資源名:
- 白名單:來源(origin)在白名單内的調用者允許通路
- 黑名單:來源(origin)在黑名單内的調用者不允許通路
接口名
流控應用:是來源者的名單。
代碼示範:
- java代碼
首先建立一個RequestOriginParser的實作類,Sentinel是通過RequestOriginParser這個接口的parseOrigin來擷取請求的來源的。
@Component
@Slf4j
public class RequestOriginParserDefinition implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String serviceName = request.getParameter("serviceName"); //接收請求參數
if (serviceName == null || "".equals("serviceName")){ //如果參數内容為空
serviceName = request.getHeader("serviceName");
}
log.info("授權資訊serviceName--->" + serviceName);
if (StringUtils.isEmpty(serviceName)){
return request.getRemoteAddr(); //根據ip位址處理
}
return serviceName;
}
}
@RequestMapping("/message1")
@SentinelResource(value = "message1", blockHandler = "insertUserBlockHandler")
public String message1() {
String message = mumberServiceImpl.message();
return "message1接口..." + "調用了" + message;
}
對于message1這個接口調用mumberServiceImpl的message()接口,在沒有配置授權規則時,是能正常通路的。
- 配置授權規則
- 采用postMan工具測試,為該接口參數serviceName設定配置的值admin
- 此處隻是采用postman進行了簡單的測試,在實際開發中,比如A服務調用B服務的某個接口,是不可能采用postman手動攜帶參數的,需要在接口中攜帶參數資訊。