1.簡介
微服務體系架構下,服務容錯是一個大事,常見的容錯方案有
-
超時
-
流控(限流)
-
熔斷降級
在業界也有很多可選擇的容錯產品,比如說服務之間調用ribbon+resttemplate支持超時設置,feign支持超時,甚至我們在設計實現api的時候,也會考慮相關的api超時機制。
比如說支持流控、熔斷降級的產品hystrix、resilience4j,以及阿里開源的sentinel。
當然今天我們的主角是sentinel,因爲它的優秀,我準備寫一個短系列文章分享介紹使用它!整理了一下,內容大致包括
-
入門使用
-
控制檯dashboard
-
spring cloud alibba 整合sentinel使用
-
擴展sentinel之錯誤頁面
-
擴展sentinel之區分來源
-
擴展sentinel之支持restful url
-
擴展sentinel之規則持久化
本篇文章是第一篇,我們從入門開始!
2.sentinel介紹
關於sentinel介紹,我想推薦你去看官網,非常詳細!這也是我想推薦給小夥伴們學習新東西的一個方式
-
一定要看官網,一定要掌握學習的方法,總結形成自己的知識體系!
-
反之,如果我們只是滿足於藉助搜索引擎翻幾篇文章,比如說吧就看我的博客,那是遠遠不夠的!
-
藉助搜索引擎,看看博客,只能是快速入門!所以還是那句話:一定要看官網
那麼官網的地址在哪呢?github,我貼一下它的地址:
https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5
順便截個圖:
總結一下,官方的介紹
-
sentinel是一個服務穩定性的防護組件,流控、熔斷降級規則豐富,應用場景豐富
-
還提供的dashboard控制面板,相關規則管理,比如說規則動態配置刷新;支持監控
-
更關鍵的是sentinel不依賴任何第三方庫,或者開源組件,這就很感人了!不管是普通sevlet web應用,老一代spring web應用,還是springboot應用,還是整合到spring cloud應用,統統都滿足!
3.入門案例
3.1.搭建環境
我這裏以springboot應用爲例,畢竟springboot今天是主流應用基礎框架。首先我們需要引入sentinel依賴
<!--公共變量定義-->
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
</properties>
<!--sentinel依賴-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.2</version>
</dependency>
這裏關於版本的選擇,我貼一個官方的地址:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
因爲我這裏選擇的springboot版本是:2.3.2.RELEASE,所以sentinel版本參照官網選擇的版本是:1.8.2
3.2.加載規則
對於sentinel防護組件,使用的關鍵是兩個點
-
聲明資源
-
定義相關規則,保護資源
3.2.1.聲明資源
/**
* <p>
* sentinel 資源
* </p>
*
* @author [email protected]
* @since 2021/8/5
*/
public interface MySentinelResource {
/**
* 流控資源
*/
public static final String FLOW_RESOURCE = "flowResource";
}
3.2.2.加載資源
/**
* <p>
* 加載sentinel規則
* 1.流控規則
* 2.降級規則
* 3.系統規則
* 4.熱點規則
* 5.授權規則
* </p>
*
* @author [email protected]
* @since 2021/8/5
*/
@Component
@Slf4j
public class LoadSentinelRules {
public LoadSentinelRules(){
// 初始化加載流控規則
initFlowRules();
log.info("加載了流控規則.");
}
/**
* 初始化流控規則
*/
public void initFlowRules(){
// 流控規則集合
List<FlowRule> flowRules = Lists.newArrayList();
FlowRule rule = new FlowRule();
rule.setResource(MySentinelResource.FLOW_RESOURCE);// 資源
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 流控閾值類型:qps
rule.setCount(1);// 流控閾值:1
flowRules.add(rule);
FlowRuleManager.loadRules(flowRules);
}
}
3.3.流控規則
編寫一個conttroller,將相關的端點作爲資源,通過sentinel進行保護
/**
* <p>
* sentinel demo controller
* </p>
*
* @author [email protected]
* @since 2021/8/5
*/
@RestController
@RequestMapping("sentinel")
@Slf4j
public class SentinelController {
@RequestMapping("flow")
public String flow(String userId){
Entry entry = null;
String result = "ok!";
try{
// 1.開始進入資源,流控保護開始
SphU.entry(MySentinelResource.FLOW_RESOURCE);
// 2.被保護的資源
log.info("SentinelController--flow,param:{}", userId);
}catch (BlockException e){
log.error("發生流控異常! msg:{}", e);
result = "error!限流了!";
}finally {
// 3.釋放資源,需要與SphU.entry配對
if(entry != null){
entry.exit();
}
}
return result;
}
}
關鍵代碼說明
-
第一步,通過SphU.entry方法聲明進入資源
-
第二步,編寫需要被保護資源
-
第三步,通過 entry.exit釋放資源
3.4.流控效果
把應用啓動起來,我們上面配置一個流控規則,表示每秒中允許通過的qps是1,超過1即需要流控(限流)
請求api:http://127.0.0.1:8080/sentinel/flow?userId=1,1秒內多次刷新
3.5.熔斷降級規則
通過上面的案例,我們看到了sentinel流控的效果。不過實現的代碼,看起來比較糟糕!你還記得這段代碼嗎?
@RequestMapping("flow")
public String flow(String userId){
Entry entry = null;
String result = "ok!";
try{
// 1.開始進入資源,流控保護開始
SphU.entry(MySentinelResource.FLOW_RESOURCE);
// 2.被保護的資源
log.info("SentinelController--flow,param:{}", userId);
}catch (BlockException e){
log.error("發生流控異常! msg:{}", e);
result = "error!限流了!";
}finally {
// 3.釋放資源,需要與SphU.entry配對
if(entry != null){
entry.exit();
}
}
return result;
}
需要通過
-
try{...}catch(BlockException e){...}finally{...}代碼塊
-
SphU.entry聲明資源開始
-
通過entry.exit聲明保護資源結束
實際項目中,如果每個需要作爲資源被保護起來的業務方法,都這麼去寫,相信小夥伴們一定很抓狂!
那麼有沒有什麼更好的實現方法呢?答案是有,sentinel給我們提供了一個@SentinelResource註解,只需要在業務方法上加上該註解,那麼sentinel就會自動把該業務方法作爲資源保護起來。
正好,通過熔斷降級規則案例,來結合@SentinelResource註解一起使用。
3.5.1.引入依賴
要使用@SentinelResource方式保護資源,需要我們引入一個新的依賴,本質上註解方式底層實現是AOP
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.2</version>
</dependency>
3.5.2.sentinel aop配置
/**
* <p>
* sentinel aop 配置
* </p>
*
* @author [email protected]
* @since 2021/8/3
*/
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
3.5.3.加載降級規則
/**
* 初始化降級規則
*/
public void initDegradeRules(){
List<DegradeRule> degradeRules = Lists.newArrayList();
DegradeRule rule = new DegradeRule();
rule.setResource(MySentinelResource.DEGRADE_RESOURCE);// 資源
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);// 閾值類型:慢調用比例
rule.setMinRequestAmount(5);// 最小請求數
rule.setTimeWindow(1);// 熔斷時長,單位秒
rule.setSlowRatioThreshold(1);// 比例閾值
rule.setCount(1);// 最大RT 單位 毫秒
rule.setStatIntervalMs(1000);// 統計時長,單位毫秒
degradeRules.add(rule);
DegradeRuleManager.loadRules(degradeRules);
}
3.5.4.編寫資源
/**
* 測試熔斷降級規則
* @return
*/
@RequestMapping("degrade")
@SentinelResource(value =MySentinelResource.DEGRADE_RESOURCE, fallback = "degradeFallback")
public String degrade(){
log.info("SentinelController--annotationDegrade start");
int time = new Random().nextInt(1000);
try {
TimeUnit.MILLISECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("SentinelController--annotationDegrade end,耗時:{}毫秒",time);
return "degrade ok!";
}
// 降級方法
public String degradeFallback(Throwable e){
log.error("熔斷降級了,異常消息:{}", e);
return "error!熔斷降級了!";
}
3.5.5.降級效果
請求:http://127.0.0.1:8080/sentinel/degrade?userId=1,且不斷刷新
3.5.6.@SentinelResource註解說明
到此,分別通過SphU.entry,與註解@SentinelResource,實現了sentinel流控、與熔斷降級的效果。我們發現@SentinelResource的方式真是好啊!代碼優雅了很多!
那麼對於該註解,做一個簡單的說明,它的關鍵屬性有
-
value:指定資源名稱
-
blockHandler:如果是流控,用於指定發生流控後的處理方法
-
fallback:如果是降級,用於指定發生降級後的處理方法
這三個是比較常用的屬性,更多屬性,推薦你去看官方文檔,連接地址:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
本文源碼地址:https://gitee.com/yanghouhua/springboot.git