2 Sentinel 限流熔斷降級 Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。我們先來學習Sentinel 核心庫的使用,後面再學習Dashboard使用。 在我們項目中,用戶 ...
2 Sentinel 限流熔斷降級
Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。我們先來學習Sentinel 核心庫的使用,後面再學習Dashboard使用。
在我們項目中,用戶請求通過hailtaxi-gateway
路由到hailtaxi-driver
或者hailtaxi-order
,還有可能在hailtaxi-order
中使用feign調用hailtaxi-driver
,所以我們有可能在單個服務中實現熔斷限流,也有可能要集成feign調用實現熔斷限流,還有可能在微服務網關中實現熔斷限流。我們接下來一步一步實現每一種熔斷限流操作。
SpringBoot集成:
如果在SpringBoot項目中使用Sentinel,首先需要引入spring-cloud-starter-alibaba-sentinel
依賴,並使用@SentinelResource
標識資源。
在hailtaxi-driver
工程中引入spring-cloud-starter-alibaba-sentinel
依賴,依賴如下:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
2.1 @SentinelResource定義資源
@SentinelResource
用於定義資源,並提供可選的異常處理和 fallback 配置項。 @SentinelResource
註解包含以下屬性:
value | 資源名稱,必需項(不能為空) |
---|---|
blockHandler / blockHandlerClass | blockHandler 對應處理 BlockException 的函數名稱,可選項。 ♞ blockHandler 函數訪問範圍需要是 public; ♞ 返回類型需要與原方法相匹配,參數類型需要和原方法相匹配並且最後加一個額外的參數,類型為 BlockException。 ♞ blockHandler 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 blockHandlerClass 為對應的類的 Class 對象,註意對應的函數必需為 static 函數,否則無法解析。 |
fallback / fallbackClass | fallback 函數名稱,可選項,用於在拋出異常的時候提供 fallback 處理邏輯。fallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore 裡面排除掉的異常類型)進行處理。fallback 函數簽名和位置要求: ♞ 返回值類型必須與原函數返回值類型一致; ♞ 方法參數列表需要和原函數一致,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。 ♞ fallback 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,註意對應的函數必需為 static 函數,否則無法解析。 |
defaultFallback(1.6.0 開始) | 預設的 fallback 函數名稱,可選項,通常用於通用的 fallback 邏輯(即可以用於很多服務或方法)。預設 fallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore 裡面排除掉的異常類型)進行處理。若同時配置了 fallback 和 defaultFallback,則只有 fallback 會生效。defaultFallback 函數簽名要求: ♞ 返回值類型必須與原函數返回值類型一致; ♞ 方法參數列表需要為空,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。 ♞ defaultFallback 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,註意對應的函數必需為 static 函數,否則無法解析。 |
exceptionsToIgnore(1.6.0 開始) | 用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣拋出。 |
entryType | entry 類型,可選項(預設為 EntryType.OUT) |
blockHandler/blockHandlerClass
在hailtaxi-driver
中找到DriverController
中的info
方法,用戶在打車的時候,會查詢司機信息,如果司機不存在,此時會報錯,代碼改造如下:
/****
* 司機信息
*/
@GetMapping(value = "/info/{id}")
//@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id,HttpServletRequest request){
log.info("當前服務占用的埠為:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
throw new RuntimeException("司機id="+id+"不存在");
}
return driver;
}
如果此時訪問:http://localhost:18081/driver/info/3 查詢司機信息,如果沒有ID為3的司機信息,會報如下錯誤,
這種體驗非常差,我們可以集成Sentinel使用@SentinelResource
的blockHandler
返回預設錯誤信息,形成降級!!!
1、Sentinel 支持在程式中拋出它定義的BlockException
異常,該異常會被Sentinel捕獲,然後走降級方法,
為info()
方法添加一個@SentinelResource
註解,用來標註資源,表示當前方法需要執行限流、降級,在註解中添加value屬性,用來標註資源,說白了就是給當前資源起個名字,blockHandler用來表示當前方法發生BlockException
異常的時候,將處理流程交給指定的方法blockExHandler()
處理,此時blockExHandler()
方法必須和拋出異常的方法在同一個類中,這是一種降級操作,代碼如下:
/****
* 司機信息
*/
@SentinelResource(value = "info",blockHandler = "blockExHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
log.info("當前服務占用的埠為:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
//throw new RuntimeException("司機id="+id+"不存在");
throw new SystemBlockException("info", "司機id="+id+"不存在",null); // 拋出BlockException
}
return driver;
}
/**
* info資源出現BlockException後的降級處理
*/
public Driver blockExHandler(String id,BlockException e) {
Driver driver = new Driver();
driver.setId(id);
driver.setName("系統繁忙,稍後再試");
return driver;
}
註意:
如果blockHandler方法和資源方法不在同一個類中,我們可以在
@SentinelResource
中添加blockHandlerClass
屬性,指定降級處理類的方法所在的類,且要求blockHandler方法是靜態的,代碼如下:@SentinelResource(value = "info",blockHandler = "blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx")
2、啟動測試,訪問:http://localhost:18081/driver/info/3 測試出錯效果如下:
fallback/fallbackClass
1、如果我們希望拋出任何異常都能處理,都能調用預設處理方法,而並非只是BlockException
異常才調用,此時可以使用@SentinelResource
的fallback
屬性,代碼如下:
/****
* 司機信息
*/
@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
log.info("當前服務占用的埠為:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
throw new RuntimeException("司機id="+id+"不存在");
// throw new SystemBlockException("info", "司機id="+id+"不存在",null); // 拋出BlockException
}
return driver;
}
/**
* info資源出現任何類型異常後的降級處理
* 方法參數可以添加一個Throwable 類型的參數,也可不添加
*/
public Driver exHandler(String id,Throwable e) {
Driver driver = new Driver();
driver.setId(id);
driver.setName("系統繁忙,稍後再試");
return driver;
}
註意:
如果fallback方法和當前的資源方法不在同一個類中,可以使用
@SentinelResource
註解的fallbackClass
實現,也要求fallback方法是靜態的,代碼如下:@SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass = "xx.xxx.xxx.xx.Xxx")
2、訪問 http://localhost:18081/driver/info/3 測試出錯效果如下:
defaultFallback
上面無論是blockHandler
還是fallback
,每個方法發生異常,都要為方法獨立創建一個處理異常的方法,效率非常低,我們可以使用@SentinelResource
註解的defaultFallback
屬性,為一個類指定一個全局的處理錯誤的方法,代碼如下:
@RestController
@RequestMapping(value = "/driver")
@Slf4j
@RefreshScope
@SentinelResource(defaultFallback = "defaultExHandler")
public class DriverController {
@Autowired
private DriverService driverService;
public Driver defaultExHandler(Throwable e) {
Driver driver = new Driver();
driver.setName("系統繁忙,稍後再試");
return driver;
}
/****
* 司機信息
*/
//@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
@SentinelResource("info")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
log.info("當前服務占用的埠為:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
throw new RuntimeException("司機id="+id+"不存在");
// throw new SystemBlockException("info", "司機id="+id+"不存在",null); // 拋出BlockException
}
return driver;
}
訪問 http://localhost:18081/driver/info/3 效果如下:
2.2 Sentinel的規則
Sentinel 的所有規則都可以在記憶體態中動態地查詢及修改,修改之後立即生效。同時 Sentinel 也提供相關 API,供您來定製自己的規則策略。
Sentinel 支持以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源訪問控制規則 和 熱點參數規則。
2.2.1 流量控制規則 (FlowRule)
流量規則的定義,重要屬性如下:
Field | 說明 | 預設值 |
---|---|---|
resource | 資源名,資源名是限流規則的作用對象 | |
count | 限流閾值 | |
grade | 限流閾值類型,QPS 模式(1)或併發線程數模式(0) | QPS 模式 |
limitApp | 流控針對的調用來源 | default ,代表不區分調用來源 |
strategy | 調用關係限流策略:直接、鏈路、關聯 | 根據資源本身(直接) |
controlBehavior | 流控效果(直接拒絕/WarmUp/勻速+排隊等待),不支持按調用關係限流 | 直接拒絕 |
clusterMode | 是否集群限流 | 否 |
同一個資源可以同時有多個限流規則,檢查規則時會依次檢查
strategy限流策略說明:
直接:資源達到限流條件時,直接限流。 關聯:A資源關聯B資源,當關聯的B資源達到閾值限流時,A資源也會被限流。 鏈路:對於某資源C,有兩個入口,從資源A->C,從資源B->C, 通過指定入口資源可以達到只記錄從該入口進來的流量(指定資源從入口資源進來的流量,如果達到閾值,就可以對其限流)。
controlBehavior流控說明:
直接拒絕:請求直接失敗。 WarmUp:當系統長期處於低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。 排隊等待:排隊處理請求。
理解上面規則的定義之後,我們可以通過調用 FlowRuleManager.loadRules()
方法來用硬編碼的方式定義流量控制規則。
QPS流量控制
1、我們先實現基於QPS流量控制,在hailtaxi-driver
的DriverApplication
啟動類上添加如下方法載入限流規則,當DriverApplication
初始化完成之後載入規則,代碼如下:
/***
* 初始化規則
*/
@PostConstruct
private void initFlowRule() {
//規則集合
List<FlowRule> rules = new ArrayList<FlowRule>();
//定義一個規則
FlowRule rule = new FlowRule("info");
// 設置閾值
rule.setCount(2);
//設置限流閾值類型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//default,代表不區分調用來源
rule.setLimitApp("default");
//設置流控效果
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
//將定義的規則添加到集合中
rules.add(rule);
//載入規則
FlowRuleManager.loadRules(rules);
}
2、訪問 http://localhost:18081/driver/info/1 此時不會拋出異常,但是頻繁刷新,則會調用降級方法,效果如下:
線程數流量控制
1、我們修改限流閾值類型,代碼如下:
@PostConstruct
public void initFlowRule() {
//規則集合
List<FlowRule> rules = new ArrayList<>();
// 定義一個規則
FlowRule rule = new FlowRule("info");
// 設置基流量控制 的類型
rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);//預設是qps
//設置流量閾值
rule.setCount(2);
// 將 規則添加到 集合中
rules.add(rule);
// 載入規則
FlowRuleManager.loadRules(rules);
}
2、此時再來訪問http://localhost:18081/driver/info/1
我們發現用瀏覽器無論怎麼訪問都不會出現降級現象,但是如果用Jmeter模擬多個線程,效果就不一樣了,效果如下:
2.2.2 熔斷降級規則 (DegradeRule)
熔斷降級規則包含下麵幾個重要的屬性:
Field | 說明 | 預設值 |
---|---|---|
resource | 資源名,即規則的作用對象 | |
grade | 熔斷策略,支持慢調用比例/異常比例/異常數策略 | 慢調用比例 |
count | 慢調用比例模式下為慢調用臨界 RT(超出該值計為慢調用);異常比例/異常數模式下為對應的閾值 | |
timeWindow | 熔斷時長,單位為 s | |
minRequestAmount | 熔斷觸發的最小請求數,請求數小於該值時即使異常比率超出閾值也不會熔斷(1.7.0 引入) | 5 |
statIntervalMs | 統計時長(單位為 ms),如 60*1000 代表分鐘級(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢調用比例閾值,僅慢調用比例模式有效(1.8.0 引入) |
同一個資源可以同時有多個降級規則。
理解上面規則的定義之後,我們可以通過調用 DegradeRuleManager.loadRules()
方法來用硬編碼的方式定義熔斷規則,
1、在DriverApplication
規則定義如下:
@PostConstruct
public void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
// 設置資源名稱
rule.setResource("info");
/**
* 設置熔斷策略
* DEGRADE_GRADE_RT:平均響應時間
* DEGRADE_GRADE_EXCEPTION_RATIO:異常比例數量
* DEGRADE_GRADE_EXCEPTION_COUNT:異常數
*/
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//設置閾值
rule.setCount(2);
//設置 熔斷時長
rule.setTimeWindow(30);
// 統計時長(單位為 ms) 預設1000
rule.setStatIntervalMs(60*1000);
//將規則添加到集合中
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
2、首先訪問:http://localhost:18081/driver/info/1 ,確保沒問題,
其次訪問:http://localhost:18081/driver/info/3,多訪問幾次,造成熔斷(5+2=7)
然後在訪問:http://localhost:18081/driver/info/1,會發現已經有熔斷降級效果了,
且查看服務控制台,發現不會有信息輸出,表明已經熔斷了,且從頁面展示效果來看走了降級
等待30s後,再次訪問:http://localhost:18081/driver/info/1,查看熔斷是否結束!
2.2.3 系統保護規則 (SystemRule)
Sentinel 系統自適應限流從整體維度對應用入口流量進行控制,結合應用的 Load、CPU 使用率、總體平均 RT、入口 QPS 和併發線程數等幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。
系統規則包含下麵幾個重要的屬性:
Field | 說明 | 預設值 |
---|---|---|
highestSystemLoad | load1 觸發值,用於觸發自適應控制階段 |
-1 (不生效) |
avgRt | 所有入口流量的平均響應時間 | -1 (不生效) |
maxThread | 入口流量的最大併發數 | -1 (不生效) |
qps | 所有入口資源的 QPS | -1 (不生效) |
highestCpuUsage | 當前系統的 CPU 使用率(0.0-1.0) | -1 (不生效) |
理解上面規則的定義之後,我們可以通過調用 SystemRuleManager.loadRules()
方法來用硬編碼的方式定義流量控制規則。
1、在hailtaxi-driver
的DriverApplication
中創建如下方法,代碼如下:
/***
* 系統自我保護
*/
@PostConstruct
private void initSystemRule() {
//系統自我保護集合
List<SystemRule> rules = new ArrayList<>();
//創建系統自我保護規則
SystemRule rule = new SystemRule();
//CPU使用率 值為[0,1],-1 (不生效)
rule.setHighestCpuUsage(0.2);
//所有入口資源的 QPS,-1 (不生效)
rule.setQps(10);
//入口流量的最大併發數,-1 (不生效)
rule.setMaxThread(5);
//所有入口流量的平均響應時間,單位:秒,-1 (不生效)
rule.setAvgRt(5);
//load1 觸發值,用於觸發自適應控制階段,系統最高負載,建議取值 CPU cores * 2.5
rule.setHighestSystemLoad(20);
//將規則加入到集合
rules.add(rule);
SystemRuleManager.loadRules(rules);
}
我們可以測試CPU使用率自我保護,使用jmeter
測試如下:
2.2.4 訪問控制規則 (AuthorityRule)
很多時候,我們需要根據調用方來限制資源是否通過,這時候可以使用 Sentinel 的訪問控制(黑白名單)的功能。黑白名單根據資源的請求來源(origin
)限制資源是否通過,若配置白名單則只有請求來源位於白名單內時才可通過;若配置黑名單則請求來源位於黑名單時不通過,其餘的請求通過。
授權規則,即黑白名單規則(AuthorityRule
)非常簡單,主要有以下配置項:
resource
:資源名,即規則的作用對象limitApp
:對應的黑名單/白名單,不同 origin 用,
分隔,如appA,appB
strategy
:限制模式,AUTHORITY_WHITE
為白名單模式,AUTHORITY_BLACK
為黑名單模式,預設為白名單模式
瞭解了以上規則後,可以通過AuthorityRuleManager.loadRules
來載入規則
1、在hailtaxi-driver
的DriverApplication
中創建如下方法,代碼如下:
@PostConstruct
public void initAuthorityRule() {
AuthorityRule rule = new AuthorityRule();
rule.setResource("info");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("127.0.0.1,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
}
/**
* Sentinel提供了 RequestOriginParser 介面來處理訪問來源,Sentinel保護的資源如果被訪問,
* 就會調用 RequestOriginParser解析訪問來源
*/
@Component
public class IpLimiter implements RequestOriginParser{
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
return httpServletRequest.getRemoteAddr();
}
}
2、訪問:http://localhost:18081/driver/info/1,不通過,走了降級
訪問:http://127.0.0.1:18081/driver/info/1
2.2.4 熱點規則 (ParamFlowRule)
何為熱點?熱點即經常訪問的數據。
很多時候我們希望統計某個熱點數據中訪問頻次最高的 Top K 數據,並對其訪問進行限制。比如:
1:商品 ID 為參數,統計一段時間內最常購買的商品 ID 併進行限制
2:用戶 ID 為參數,針對一段時間內頻繁訪問的用戶 ID 進行限制
熱點參數限流會統計傳入參數中的熱點參數,並根據配置的限流閾值與模式,對包含熱點參數的資源調用進行限流。熱點參數限流可以看做是一種特殊的流量控制,僅對包含熱點參數的資源調用生效。
Sentinel 利用 LRU 策略統計最近最常訪問的熱點參數,結合令牌桶演算法來進行參數級別的流控。熱點參數限流支持集群模式。
要使用熱點參數限流功能,需要引入以下依賴,將該依賴加入到hailtaxi-driver
中:
<!--熱點參數限流-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.8.1</version>
</dependency>
然後為對應的資源配置熱點參數限流規則,併在 entry
的時候傳入相應的參數,即可使熱點參數限流生效。
熱點參數規則(ParamFlowRule
)類似於流量控制規則(FlowRule
):
屬性 | 說明 | 預設值 |
---|---|---|
resource | 資源名,必填 | |
count | 限流閾值,必填 | |
grade | 限流模式 | QPS 模式 |
durationInSec | 統計視窗時間長度(單位為秒),1.6.0 版本開始支持 | 1s |
controlBehavior | 流控效果(支持快速失敗和勻速排隊模式),1.6.0 版本開始支持 | 快速失敗 |
maxQueueingTimeMs | 最大排隊等待時長(僅在勻速排隊模式生效),1.6.0 版本開始支持 | 0ms |
paramIdx | 熱點參數的索引,必填,對應 SphU.entry(xxx, args) 中的參數索引位置 |
|
paramFlowItemList | 參數例外項,可以針對指定的參數值單獨設置限流閾值,不受前面 count 閾值的限制。僅支持基本類型和字元串類型 |
|
clusterMode | 是否是集群參數流控規則 | false |
clusterConfig | 集群流控相關配置 |
我們可以通過 ParamFlowRuleManager
的 loadRules
方法更新熱點參數規則
1、在DriverController
中創建一個司機篩選方法,比如根據城市來篩選,在DriverController
中創建一個方法:
/***
* 搜素指定城市的司機
*/
@SentinelResource(value = "search")
@GetMapping(value = "/search/{city}")
public Driver search(@PathVariable(value = "city")String city){
System.out.println("查詢的司機所在城市:"+city);
//假設查詢到了一個司機信息
Driver driver = new Driver();
driver.setName("唐僧老師");
driver.setId("No.1");
return driver;
}
2、對熱門參數進行控制,對熱點數據執行特殊限流,比如資源參數列表中的第一個參數值為恩施
的時候執行限流,在DriverApplication
中創建限流配置,代碼如下:
/***
* 熱點參數初始化
*/
@PostConstruct
private static void initParamFlowRules() {
ParamFlowRule rule = new ParamFlowRule("search")
//參數下標為0
.setParamIdx(0)
//限流模式為QPS
.setGrade(RuleConstant.FLOW_GRADE_QPS)
//統計視窗時間長度(單位為秒)
.setDurationInSec(10)
//流控效果(支持快速失敗和勻速排隊模式)
//CONTROL_BEHAVIOR_DEFAULT:限流行為,直接拒絕
//CONTROL_BEHAVIOR_WARM_UP:限流行為,勻速排隊
//CONTROL_BEHAVIOR_RATE_LIMITER:限流行為,勻速排隊
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
//最大排隊等待時長(僅在勻速排隊模式生效 CONTROL_BEHAVIOR_RATE_LIMITER)
//.setMaxQueueingTimeMs(600)
//最大閾值為5
.setCount(5);
// 為特定參數單獨設置規則
//如下配置:當下標為0的參數值為恩施的時候,閾值到達2的時候則執行限流
ParamFlowItem item = new ParamFlowItem()
//參數類型為String類型
.setClassType(String.class.getName())
//設置閾值為2
.setCount(2)
//需要統計的值
.setObject(String.valueOf("恩施"));
rule.setParamFlowItemList(Collections.singletonList(item)); //返回的是不可變的集合,但是這個長度的集合只有1,可以減少記憶體空間
//載入熱點數據
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
2、我們訪問 http://localhost:18081/driver/search/天津/ 的時候,連續執行5次,才會限流,
我們訪問 http://localhost:18081/driver/search/恩施/ 的時候,連續執行2次,就會限流,
2.3 OpenFeign支持
Sentinel 適配了 Feign 組件。如果想使用,除了外還需要 2 個步驟:
1:引入 `spring-cloud-starter-alibaba-sentinel` 的依賴
2:加入 spring-cloud-starter-openfeign 依賴
3:配置文件打開 Sentinel 對 Feign 的支持:feign.sentinel.enabled=true
在上面案例中,我們可以實現用戶打車成功調用hailtaxi-order
執行下單,並且通過feign調用hailtaxi-driver
修改司機狀態,此時我們可以使用Sentinel實現Feign調用降級、限流。
註意:
在進行操作之前,可以先將
hailtaxi-driver
中的訪問控制規則註釋掉,註釋掉DriverApplication#initAuthorityRule
,DriverApplication#initSystemRule
上的註解即可
1、在hailtaxi-order
中引入sentinel
和OpenFeign
依賴,配置如下:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!--Openfeign api模塊中已存在也可不引入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、在hailtaxi-order
的配置文件中開啟Feign支持sentinel
,配置如下:
feign:
#開啟Sentinel對Feign的支持
sentinel:
enabled: true
註意:現在配置信息都存放在了
nacos
中,所以找到hailtaxi-order.yaml
的Data ID配置,將以上配置添加進去!註意修改完後一定要發佈才能生效!!!
3、因為hailtaxi-order
要通過openfeign
去調用hailtaxi-driver
中的DriverController#status
方法,改造一下該方法
/****
* 更新司機信息
*/
@PutMapping(value = "/status/{id}/{status}")
public Driver status(@PathVariable(value = "id")String id,@PathVariable(value = "status")Integer status) throws Exception {
log.info("當前服務占用的埠為:{}",port);
//修改狀態
driverService.update(id,status);
//修改狀態後的司機信息
Driver driver = driverService.findById(id);
if (driver == null) {
throw new RuntimeException("學生id="+id+",不存在");
}
return driver;
}
模擬被調用服務出現異常的情況
3、先驗證正確性,啟動hailtaxi-gateway
,hailtaxi-order
,hailtaxi-driver
服務,使用postman訪問:
4、為了測試程式異常能實現降級操作,我們在hailtaxi-order
中將OrderInfoController.add()
方法的司機ID改成一個不存在的司機ID,讓程式報錯,測試降級處理,代碼如下:
/***
* 下單
*/
@PostMapping
public OrderInfo add(){
//修改司機信息 司機ID=1
Driver driver = driverFeign.status("3",2);// 改成一個不存在的id
//創建訂單
OrderInfo orderInfo = new OrderInfo("No"+((int)(Math.random()*10000)), (int)(Math.random()*100), new Date(), "深圳北站", "羅湖港", driver);
orderInfoService.add(orderInfo);
return orderInfo;
}
5、再次啟動測試:
註意:服務調用出錯要進行降級操作有兩個地方都可以進行,一是在被調用方進行降級,一是在調用方進行降級,
在被調用方進行降級前面已經講過了,所以接下來講解如何在調用方(openfeign)結合 sentinel 進行降級處理。
2.3.1 fallback
在hailtaxi-api
模塊中找到DriverFeign
介面:
1、為Feign介面創建一個實現類:com.itheima.driver.feign.fallback.DriverFeignFallback
,在實現類中處理程式異常降級處理方法,代碼如下:
package com.itheima.driver.feign.fallback;
import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import org.springframework.stereotype.Component;
@Component
public class DriverFeignFallback implements DriverFeign {
/**
* status()降級處理方法
*/
@Override
public Driver status(String id, Integer status) {
Driver driver = new Driver();
driver.setId(id);
driver.setStatus(status);
driver.setName("系統比較繁忙,請您稍後再試!");
return driver;
}
}
2、在DriverFeign
介面上添加fallback
屬性指定降級處理的類,代碼如下:
@FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)
註意:修改了
hailtaxi-api
模塊,最好clean
,package
!!
3、啟動運行,會發生如下問題:
java.lang.AbstractMethodError: com.alibaba.cloud.sentinel.feign.SentinelContractHolder.parseAndValidatateMetadata(Ljava/lang/Class;)Ljava/util/List;
出現上面問題的主要原因是當前SpringCloud版本存在問題。
Hoxton.SR1
中,fegin.context
介面方法的定義為parseAndValidatateMetadata
Hoxton.SR3
中,fegin.context
介面方法的定義為parseAndValidateMetadata
我們現在需要把Hoxton.SR1
換成Hoxton.SR3
,因此需要在hailtaxi-parent
修改SpringCloud版本:
將SpringCloud
版本升級至Hoxton.SR3
同時將SpringBoot
版本升級至2.2.10
,如上圖:
https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-Hoxton-Release-Notes
此時我們測試,效果如下:
2.3.2 fallbackFactory
我們可以為DriverFeign
介面創建一個降級處理的工廠對象,在工廠對象中處理程式異常降級處理方法,用工廠對象處理可以拿到異常信息,代碼如下:
1、創建:com.itheima.driver.feign.fallback.DriverFeignFallbackFactory
package com.itheima.driver.feign.fallback;
import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class DriverFeignFallbackFactory implements FallbackFactory<DriverFeign> {
@Override
public DriverFeign create(Throwable throwable) {
return new DriverFeign() {
/**
* status()降級處理方法
*/
@Override
public Driver status(String id, Integer status) {
Driver driver = new Driver();
driver.setId(id);
driver.setStatus(status);
driver.setName("系統比較繁忙,請您稍後再試!");
return driver;
}
};
}
}
2、在DriverFeign
介面上添加fallbackFactory
屬性指定講解處理的類,代碼如下:
@FeignClient(value = "hailtaxi-driver",fallbackFactory = DriverFeignFallbackFactory.class)
3、再次啟動測試,效果如下:
好了,接下來,咱麽來說一說Sentinel控制台
4 Sentinel控制台
Sentinel 控制台是流量控制、熔斷降級規則統一配置和管理的入口,它為用戶提供了機器自發現、簇點鏈路自發現、監控、規則配置等功能。在 Sentinel 控制臺上,我們可以配置規則並實時查看流量控制效果。
4.1 Sentinel控制台安裝
Sentinel 提供一個輕量級的開源控制台,它提供機器發現以及健康情況管理、監控(單機和集群),規則管理和推送的功能。
Sentinel 控制台包含如下功能:
- 查看機器列表以及健康情況:收集 Sentinel 客戶端發送的心跳包,用於判斷機器是否線上。
- 監控 (單機和集群聚合):通過 Sentinel 客戶端暴露的監控 API,定期拉取並且聚合應用監控信息,最終可以實現秒級的實時監控。
- 規則管理和推送:統一管理推送規則。
- 鑒權:生產環境中鑒權非常重要。這裡每個開發者需要根據自己的實際情況進行定製。
Sentinel控制台安裝可以基於jar包啟動的方式安裝,也可以基於docker安裝,為了方便操作,我們這裡採用docker安裝方式:
docker run --name=sentinel-dashboard -d -p 8858:8858 -d --restart=on-failure bladex/sentinel-dashboard:1.8.0
安裝好了後,我們可以直接訪問 http://192.168.200.129:8858/,預設用戶名和密碼都是 sentinel
登錄後,效果如下:
4.2 接入控制台
客戶端需要引入 Transport 模塊來與 Sentinel 控制台進行通信,可以通過
pom.xml
引入 JAR 包:<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>x.y.z</version> </dependency>
如果是SpringBoot工程接入Sentinel,可以直接引入如下依賴包:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.2.5.RELEASE</version> </dependency>
hailtaxi-gateway接入控制台:
1、引入依賴包:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
2、在核心配置文件中配置Sentinel控制台服務地址
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: 192.168.200.129:8858
註意:
1、這裡的
spring.cloud.sentinel.transport.port
埠配置會在應用對應的機器上啟動一個 Http Server,該 Server 會與 Sentinel 控制台做交互,比如限流規則拉取。2、配置信息現在是存儲到nacos中,故要在nacos中找到
hailtaxi-gateway.yaml
,進行如下配置修改之後記得發佈!
3、啟動各個服務,
此時我們出發一些請求操作,再看Sentinel控制台會多一個服務監控:
4.3 可視化管理
4.3.1 實時監控
同一個服務下的所有機器的簇點信息會被彙總,並且秒級地展示在"實時監控"下。
註意: 實時監控僅存儲 5 分鐘以內的數據,如果需要持久化,需要通過調用實時監控介面來定製。
如果要獲取監控數據,直接調用 http://localhost:8719/clusterNode 即可獲取,效果如下:
4.3.2 流控規則
我們可以在【流控規則】頁面中新增,點擊【流控規則】進入頁面新增頁面,如下圖:
資源名:其實可以和請求路徑保持一致,這裡的流控模式為QPS,觸發流控執行閾值為1,流控模式為讓當前請求的資源快速直白。
這裡的參數和我們程式中的參數其實是一樣的,如下說明:
resource:資源名,即限流規則的作用對象 count: 限流閾值 grade: 限流閾值類型(QPS 或併發線程數) limitApp: 流控針對的調用來源,若為 default 則不區分調用來源 strategy: 調用關係限流策略:直接,關聯,鏈路 controlBehavior: 流量控制效果(直接拒絕、Warm Up、勻速排隊)
我們測試效果如下:
4.3.3 降級規則
我們可以選擇降級規則>新增降級規則
,如下圖:
降級規則的熔斷策略有3種,分別是慢調用比例、異常比例、異常數,和程式中是一樣的。
4.3.4 熱點數據
熱點即經常訪問的數據。很多時候我們希望統計某個熱點數據中訪問頻次最高的 Top K 數據,並對其訪問進行限制。
拓展知識點:
註意:如果流控模式是鏈路模式,需要引入如下依賴:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>1.8.0</version>
</dependency>
創建過濾器:
/***
* CommonFilter過濾器
* @return
*/
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*"); //過濾所有請求
// 入口資源關閉聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
bootstrap.yml配置:web-context-unify: false
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8858
web-context-unify: false #可根據不同的URL 進行鏈路限流
本文由傳智教育博學谷 - 狂野架構師教研團隊發佈
轉載請註明出處!