sentinel流量控制
Sentinel流量控制&服務熔斷降級介紹
流量控制介紹

在這里我用景區的例子解釋一下
一個旅游景點日接待游客數量為8K,8K以后的游客就無法買票進去景區。
對應編程來說就是,一個接口QPS(每秒請求數)最大為100,在QPS100之后的請求我們就要限制其訪問,並給出友好提示。
不限制QPS無限的次數就會造成服務器的宕機。
服務熔斷降級
在調用系統的時候,如果調用鏈路中的某個資源出現了不穩定,最終會導致請求發生堆積,進而導致級聯錯誤
而熔斷降級就可以解決這個問題,所謂的熔斷降級就是當檢測到調用鏈路中某個資源出現不穩定的表現,例如請求響應時間長或異常比例升高的時候,則對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導致級聯故障
sentinel流量控制入門
創建本地應用
整體流程分析
- 創建springBoot項目
- 導入sentinel的坐標(此處使用為gradle)
- 創建Controller測試接口,定義限流規則同時引用限流規則
導入核心依賴

implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel")
本地Sentinel控制台
下載sentinel控制台jar包
啟動Sentinel控制台
啟動Sentinel控制台需要JDK版本為1.8及以上版本。
使用一下命令啟動
java -Dserver.port=端口號 -jar sentinel-dashboard-1.8.0.jar
訪問控制台
通過瀏覽器訪問http://localhost:端口號即可訪問控制台,默認用戶名密碼都是sentinel

Sentinel自定義資源方式
- 拋出異常的方式定義資源
- 返回布爾值方式定義資源
- 異步調用支持
- 注解方式定義資源
- 主流框架的默認適配
拋出異常的方式定義資源
Sentinel種的SphU
包含了try-catch
風格的API。用這種方式,當資源發生了限流之后會拋出BlockException
。
這個時候就可以捕獲異常,進行限流之后的邏輯操作,在入門案例種就是使用此方式實現的,關鍵代碼:
fun hello(): String {
//使用限流規則
try {
SphU.entry("Hello")
} catch (e: Exception) {
return "系統繁忙,請稍后再試!!"
}
return "Hello Sentinel"
}
特別地,若 entry 的時候傳入了熱點參數,那么 exit 的時候也一定要帶上對應的參數(exit(count, args)
),否則可能會有統計錯誤。這個時候不能使用 try-with-resources 的方式。另外通過 Tracer.trace(ex)
來統計異常信息時,由於 try-with-resources 語法中 catch 調用順序的問題,會導致無法正確統計異常數,因此統計異常信息時也不能在 try-with-resources 的 catch 塊中調用 Tracer.trace(ex)
。
手動 exit 示例:
Entry entry = null;
// 務必保證 finally 會被執行
try {
// 資源名可使用任意有業務語義的字符串,注意數目不能太多(超過 1K),超出幾千請作為參數傳入而不要直接作為資源名
// EntryType 代表流量類型(inbound/outbound),其中系統規則只對 IN 類型的埋點生效
entry = SphU.entry("自定義資源名");
// 被保護的業務邏輯
// do something...
} catch (BlockException ex) {
// 資源訪問阻止,被限流或被降級
// 進行相應的處理操作
} catch (Exception ex) {
// 若需要配置降級規則,需要通過這種方式記錄業務異常
Tracer.traceEntry(ex, entry);
} finally {
// 務必保證 exit,務必保證每個 entry 與 exit 配對
if (entry != null) {
entry.exit();
}
}
熱點參數埋點示例:
Entry entry = null;
try {
// 若需要配置例外項,則傳入的參數只支持基本類型。
// EntryType 代表流量類型,其中系統規則只對 IN 類型的埋點生效
// count 大多數情況都填 1,代表統計為一次調用。
entry = SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
// Your logic here.
} catch (BlockException ex) {
// Handle request rejection.
} finally {
// 注意:exit 的時候也一定要帶上對應的參數,否則可能會有統計錯誤。
if (entry != null) {
entry.exit(1, paramA, paramB);
}
}
SphU.entry()
的參數描述:
參數名 | 類型 | 解釋 | 默認值 |
---|---|---|---|
entryType | EntryType |
資源調用的流量類型,是入口流量(EntryType.IN )還是出口流量(EntryType.OUT ),注意系統規則只對 IN 生效 |
EntryType.OUT |
count | int |
本次資源調用請求的 token 數目 | 1 |
args | Object[] |
傳入的參數,用於熱點參數限流 | 無 |
注意:SphU.entry(xxx)
需要與 entry.exit()
方法成對出現,匹配調用,否則會導致調用鏈記錄異常,拋出 ErrorEntryFreeException
異常。常見的錯誤:
- 自定義埋點只調用
SphU.entry()
,沒有調用entry.exit()
- 順序錯誤,比如:
entry1 -> entry2 -> exit1 -> exit2
,應該為entry1 -> entry2 -> exit2 -> exit1
返回布爾值方式定義資源
Sentinel種的SphO
包含了if-else
風格的API。用這種方式,當資源發生了限流之后會返回 false
,這時候可以根據返回值,進行限流之后的邏輯處理。
-
在項目中創建TestBooleanController中使用返回booelan的方式定義資源
@GetMapping("sentinel_boolean") fun hello(): Boolean { //配置限流入口,在這里是使用控制台進行配置限流規則 return if (SphO.entry("Boolean")) { try {//被保護的資源 println("Hello Sentinel Boolean") true } finally { //限流出口 SphO.exit() } } else { //限流或者降級處理 println("系統繁忙,請稍后重試") false } }
**在控制台進行流控規則的配置 **

注意 SphO.entry(xxx)
需要於SphO.exit()
方法成對出現,匹配調用,位置正確,否則就會導致調用鏈記錄異常,拋出ErrorEntryFreeException
異常
異步調用支持
Sentinel 支持異步調用鏈路的統計。在異步調用中,需要通過 SphU.asyncEntry(xxx)
方法定義資源,並通常需要在異步的回調函數中調用 exit
方法。
- 啟動類開啟異步調用
@EnableAsync
- 開啟方法的異步調用
@Async
SphU.asyncEntry(xxx)
不會影響當前(調用線程)的 Context,因此以下兩個 entry 在調用鏈上是平級關系(處於同一層),而不是嵌套關系:
// 調用鏈類似於:
// -parent
// ---asyncResource
// ---syncResource
asyncEntry = SphU.asyncEntry(asyncResource);
entry = SphU.entry(normalResource);
若在異步回調中需要嵌套其它的資源調用(無論是 entry
還是 asyncEntry
),只需要借助 Sentinel 提供的上下文切換功能,在對應的地方通過 ContextUtil.runOnContext(context, f)
進行 Context 變換,將對應資源調用處的 Context 切換為生成的異步 Context,即可維持正確的調用鏈路關系。示例如下:
public void handleResult(String result) {
Entry entry = null;
try {
entry = SphU.entry("handleResultForAsync");
// Handle your result here.
} catch (BlockException ex) {
// Blocked for the result handler.
} finally {
if (entry != null) {
entry.exit();
}
}
}
public void someAsync() {
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// Asynchronous invocation.
doAsync(userId, result -> {
// 在異步回調中進行上下文變換,通過 AsyncEntry 的 getAsyncContext 方法獲取異步 Context
ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
try {
// 此處嵌套正常的資源調用.
handleResult(result);
} finally {
entry.exit();
}
});
});
} catch (BlockException ex) {
// Request blocked.
// Handle the exception (e.g. retry or fallback).
}
}
此時的調用鏈就類似於:
-parent
---asyncInvocation
-----handleResultForAsync
更詳細的示例可以參考 Demo 中的 AsyncEntryDemo,里面包含了普通資源與異步資源之間的各種嵌套示例。
注解方式定義資源
Sentinel 支持通過 @SentinelResource
注解定義資源並配置 blockHandler
和 fallback
函數來進行限流之后的處理。使用 Sentinel Annotation AspectJ Extension 的時候需要引入以下依賴
implementation("com.alibaba.csp:sentinel-annotation-aspectj:1.7.2")
@RestController
class TestAnnController {
/**
* SentinelResource: 定義支援
* value:設置資源的名稱
* blockHandler:設置降級或者限流的處理函數
*/
@SentinelResource(value = "sentinel_ann", blockHandler = "exceptionHandler")
@GetMapping("ann")
fun hello(): String{
return "hello Sentinel"
}
/**
* 限流或者降級的函數
*/
fun exceptionHandler(ex: BlockException) :String {
ex.printStackTrace()
return "系統繁忙,請稍后"
}
}
@SentinelResource 注解
注意:注解方式埋點不支持 private 方法。
@SentinelResource
用於定義資源,並提供可選的異常處理和 fallback 配置項。 @SentinelResource
注解包含以下屬性:
value
:資源名稱,必需項(不能為空)entryType
:entry 類型,可選項(默認為EntryType.OUT
)blockHandler
/blockHandlerClass
:blockHandler
對應處理BlockException
的函數名稱,可選項。blockHandler 函數訪問范圍需要是public
,返回類型需要與原方法相匹配,參數類型需要和原方法相匹配並且最后加一個額外的參數,類型為BlockException
。blockHandler 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定blockHandlerClass
為對應的類的Class
對象,注意對應的函數必需為 static 函數,否則無法解析。fallback
/fallbackClass
:fallback 函數名稱,可選項,用於在拋出異常的時候提供 fallback 處理邏輯。fallback 函數可以針對所有類型的異常(除了exceptionsToIgnore
里面排除掉的異常類型)進行處理。fallback 函數簽名和位置要求:- 返回值類型必須與原函數返回值類型一致;
- 方法參數列表需要和原函數一致,或者可以額外多一個
Throwable
類型的參數用於接收對應的異常。 - fallback 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定
fallbackClass
為對應的類的Class
對象,注意對應的函數必需為 static 函數,否則無法解析。
defaultFallback
(since 1.6.0):默認的 fallback 函數名稱,可選項,通常用於通用的 fallback 邏輯(即可以用於很多服務或方法)。默認 fallback 函數可以針對所有類型的異常(除了exceptionsToIgnore
里面排除掉的異常類型)進行處理。若同時配置了 fallback 和 defaultFallback,則只有 fallback 會生效。defaultFallback 函數簽名要求:- 返回值類型必須與原函數返回值類型一致;
- 方法參數列表需要為空,或者可以額外多一個
Throwable
類型的參數用於接收對應的異常。 - defaultFallback 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定
fallbackClass
為對應的類的Class
對象,注意對應的函數必需為 static 函數,否則無法解析。
exceptionsToIgnore
(since 1.6.0):用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣拋出。
1.8.0 版本開始,defaultFallback
支持在類級別進行配置。
注:1.6.0 之前的版本 fallback 函數只針對降級異常(
DegradeException
)進行處理,不能針對業務異常進行處理。
特別地,若 blockHandler 和 fallback 都進行了配置,則被限流降級而拋出 BlockException
時只會進入 blockHandler
處理邏輯。若未配置 blockHandler
、fallback
和 defaultFallback
,則被限流降級時會將 BlockException
直接拋出(若方法本身未定義 throws BlockException 則會被 JVM 包裝一層 UndeclaredThrowableException
)。
示例:
public class TestService {
// 原函數
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public String hello(long s) {
return String.format("Hello at %d", s);
}
// Fallback 函數,函數簽名與原函數一致或加一個 Throwable 類型的參數.
public String helloFallback(long s) {
return String.format("Halooooo %d", s);
}
// Block 異常處理函數,參數最后多一個 BlockException,其余與原函數一致.
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
// 這里單獨演示 blockHandlerClass 的配置.
// 對應的 `handleException` 函數需要位於 `ExceptionUtil` 類中,並且必須為 public static 函數.
@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
public void test() {
System.out.println("Test");
}
}
從 1.4.0 版本開始,注解方式定義資源支持自動統計業務異常,無需手動調用 Tracer.trace(ex)
來記錄業務異常。Sentinel 1.4.0 以前的版本需要自行調用 Tracer.trace(ex)
來記錄業務異常。
配置
Spring Cloud Alibaba
若您是通過 Spring Cloud Alibaba 接入的 Sentinel,則無需額外進行配置即可使用 @SentinelResource
注解。
Spring AOP
若您的應用使用了 Spring AOP(無論是 Spring Boot 還是傳統 Spring 應用),您需要通過配置的方式將 SentinelResourceAspect
注冊為一個 Spring Bean:
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
我們提供了 Spring AOP 的示例,可以參見 sentinel-demo-annotation-spring-aop。
AspectJ
若您的應用直接使用了 AspectJ,那么您需要在 aop.xml
文件中引入對應的 Aspect:
<aspects>
<aspect name="com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect"/>
</aspects>
主流框架的默認適配

文檔地址:https://github.com/alibaba/Sentinel/wiki/主流框架的適配#spring-cloud
規則的種類
Sentinel 的所有規則都可以在內存態中動態地查詢及修改,修改之后立即生效。同時 Sentinel 也提供相關 API,供您來定制自己的規則策略。
Sentinel 支持以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源訪問控制規則 和 熱點參數規則。
流量控制規則 (FlowRule)
流量規則的定義
重要屬性:
Field | 說明 | 默認值 |
---|---|---|
resource | 資源名,資源名是限流規則的作用對象 | |
count | 限流閾值 | |
grade | 限流閾值類型,QPS 模式(1)或並發線程數模式(0) | QPS 模式 |
limitApp | 流控針對的調用來源 | default ,代表不區分調用來源 |
strategy | 調用關系限流策略:直接、鏈路、關聯 | 根據資源本身(直接) |
controlBehavior | 流控效果(直接拒絕/WarmUp/勻速+排隊等待),不支持按調用關系限流 | 直接拒絕 |
clusterMode | 是否集群限流 | 否 |
同一個資源可以同時有多個限流規則,檢查規則時會依次檢查。
通過代碼定義流量控制規則
理解上面規則的定義之后,我們可以通過調用 FlowRuleManager.loadRules()
方法來用硬編碼的方式定義流量控制規則,比如:
private void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule(resourceName);
// set limit qps to 20
rule.setCount(20);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
}