Hi拯田,我是空夜旬昭!
本節(jié)示例代碼在 https://github.com/laolunsi/spring-boot-examples
首先下載 sentinel jar包:https://github.com/alibaba/Sentinel/releases
java -jar sentinel-xx.jar 運(yùn)行著恩,打開(kāi)瀏覽器题山,輸入默認(rèn)地址:http://localhost:8080
sentinel 進(jìn)行流量控制有以下流程:
- 定義資源
- 定義規(guī)則
- 檢驗(yàn)規(guī)則是否生效
概念
資源秀又,是 sentinel 的核心概念之一涮帘,可以簡(jiǎn)單的理解為一段代碼拼苍。
sentinel 對(duì)主流的框架都提供了適配,下面以 Spring Cloud 為例,記錄在 Spring Cloud 微服務(wù)架構(gòu)中如何使用 sentinel 進(jìn)行流量控制疮鲫。
資源
分為以下幾種方式:
- 主流框架的默認(rèn)配置
- 拋出異常方式定義資源
- 返回布爾值方式定義資源
- 注解方式定義資源
- 異步調(diào)用支持
注解方式定義資源主要用于接口上吆你,對(duì)接口進(jìn)行流量控制,使用的是 @ResourceSentinel 注解俊犯。該注解提供了可選的異常處理和 fallback 配置項(xiàng)妇多。參考:Sentinel 注解支持
源碼:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
// 資源名稱,不能為空
String value() default "";
EntryType entryType() default EntryType.OUT;
int resourceType() default 0;
// 可選項(xiàng)燕侠,處理 BlockException 的函數(shù)名稱者祖,默認(rèn)在本類中;如果想要指定其他類中的函數(shù)绢彤,需要使用 blockHandlerClass 屬性
String blockHandler() default "";
Class<?>[] blockHandlerClass() default {};
// 可選項(xiàng)七问,fallback 函數(shù)名稱,用于在拋出異常時(shí)提供 fallback 處理邏輯茫舶⌒笛玻可以針對(duì)所有類型的異常。配合 fallbackClass 屬性可以指定其他類中的函數(shù)
String fallback() default "";
String defaultFallback() default "";
Class<?>[] fallbackClass() default {};
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
// 忽略異常饶氏,即 fallback 和 blockHandler 函數(shù)不起作用的異常
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
注意:
- blockHandler 和 fallback 指定的函數(shù)默認(rèn)在本類中坟比,該函數(shù)的返回值類型和參數(shù)順序需與原方法保持一致。blockHandler 對(duì)應(yīng)的函數(shù)可以在最后額外加一個(gè) BlockException 類型的參數(shù)嚷往,fallback 對(duì)應(yīng)的函數(shù)可以再最后額外加一個(gè) Throwable 類型的參數(shù)
- 使用對(duì)應(yīng)的 blockHandlerClass 和 fallbackClass 可以指定對(duì)應(yīng)函數(shù)所在的類葛账,而不是默認(rèn)的當(dāng)前類。注意皮仁,此時(shí)籍琳,指定的函數(shù)需要是 static 的,否則無(wú)法解析贷祈。
規(guī)則
- 流量控制規(guī)則 FlowRule:流量控制
原理是監(jiān)控應(yīng)用流量的 QPS 或并發(fā)線程數(shù)等指標(biāo)趋急,設(shè)置閾值,避免服務(wù)被瞬時(shí)的高峰流量沖垮势誊。目的是保障高峰流量時(shí)應(yīng)用的高可用性呜达。
- 熔斷降級(jí)規(guī)則 DegradeRule:熔斷降級(jí)
目的是防止單個(gè)服務(wù)不可用導(dǎo)致的雪崩現(xiàn)象的發(fā)生,同樣是保障應(yīng)用高可用性的方法之一粟耻。
針對(duì)的是調(diào)用鏈路中不穩(wěn)定的資源查近。
- 系統(tǒng)保護(hù)規(guī)則 SystemRule:系統(tǒng)自適應(yīng)限流
系統(tǒng)保護(hù)規(guī)則從應(yīng)用級(jí)別(而不是資源維度)的入口流量進(jìn)行控制,從單臺(tái)機(jī)器的 load挤忙、CPU 使用率霜威、平均 RT、入口 QPS 和并發(fā)線程等幾個(gè)維度監(jiān)控應(yīng)用指標(biāo)册烈,讓系統(tǒng)盡可能跑在最大吞吐量的同時(shí)保證系統(tǒng)整體的穩(wěn)定性戈泼。
- 來(lái)源訪問(wèn)控制規(guī)則 AuthorityRule:黑白名單控制
根據(jù)調(diào)用來(lái)源判斷請(qǐng)求是否允許通過(guò)。
- 熱點(diǎn)參數(shù)規(guī)則 ParamFlowRule:熱點(diǎn)參數(shù)限流
看作一種特殊的流量控制規(guī)則,僅對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用生效大猛。它根據(jù)傳入?yún)?shù)中的熱點(diǎn)參數(shù)扭倾,與配置的限流閾值、模式挽绩,對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用進(jìn)行限流吆录。
sentinel 還提供了相關(guān) API 用于定制自己的規(guī)則策略。
上面默認(rèn)的規(guī)則分類琼牧,可以通過(guò)代碼定義,也可以使用 dashboard 定義哀卫。
定義規(guī)則可以通過(guò) dashboard巨坊,也可以使用代碼創(chuàng)建。如果想要持久化存儲(chǔ)規(guī)則此改,可以利用 Nacos 等數(shù)據(jù)源趾撵。這一點(diǎn)在后面會(huì)講。
Spring Cloud 整合 Sentinel
參考:spring-cloud-alibaba-sentinel
創(chuàng)建一個(gè) demo 服務(wù)共啃,引入 spring-cloud 中的 sentinel 依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>0.9.0.RELEASE</version>
</dependency>
配置:
server:
port: 8203
spring:
application:
name: demo
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8081
可以配置流控占调、降級(jí)、熱點(diǎn)移剪、授權(quán)多種規(guī)則:
注解支持使用參考:注解支持 —— 官方文檔
默認(rèn)情況下是以 url 作為鏈路地址以及資源名的究珊,我們可以在代碼中用 @SentinelResource 注解來(lái)主動(dòng)注明資源的名稱、阻塞處理方法等纵苛。比如:
@RestController
@RequestMapping(value = "test")
@Validated
public class TestAction {
private static Logger logger = LoggerFactory.getLogger(TestAction.class);
@SentinelResource(value = "helloTest", blockHandler = "handleEx")
@GetMapping(value = "hello")
public String hello(@RequestParam("msg") @NotBlank String msg) {
logger.info("hellow, " + msg);
return "hello, " + msg;
}
@GetMapping(value = "send")
@SentinelResource(value = "sendTest", blockHandler = "handleException", blockHandlerClass = BlockHandlerConfig.class)
public String send(@NotBlank(message = "{required}") String email, @Email(message = "{invalid}") String msg) {
return "發(fā)消息給:" + email + "剿涮,消息內(nèi)容:" + msg;
}
public String handleEx(String msg, BlockException ex) {
System.out.println("系統(tǒng)錯(cuò)誤:msg=" + msg + ", ex: " + ex);
return "流量限制,請(qǐng)稍后重試";
}
}
這里的 helloTest 使用了本類中的 handleEx 方法作為阻塞處理方法攻人。而 sendTest 用 BlockHandlerConfig 類中的 handleException 方法:
public class BlockHandlerConfig {
public static String handleException(String email, String msg, BlockException exception) {
return "444, " + exception.getClass().getCanonicalName() + "\t 服務(wù)不可用";
}
}
需要注意的是取试,blockHandle 對(duì)應(yīng)方法的參數(shù)必須與 資源
的參數(shù)保持一致,否則規(guī)則不會(huì)生效怀吻,且會(huì)拋出異常瞬浓。
這里給 helloTest 這個(gè) resource 配置一個(gè)簡(jiǎn)單的流控:
測(cè)試:
快速請(qǐng)求 helloTest 對(duì)應(yīng)的接口,發(fā)現(xiàn)成功請(qǐng)求與限流響應(yīng)交錯(cuò)出現(xiàn)了蓬坡。這表明我們的限流規(guī)則和 blockHandler 生效了猿棉。
規(guī)則持久化
需要注意的是,如果服務(wù)重啟了屑咳,那么這些規(guī)則配置就會(huì)被丟失铺根。其中一個(gè)解決辦法是利用 Nacos 做配置中心,先將規(guī)則定義保存在 Nacos 中乔宿。
sentinel 提供了 file/nacos/zp/apollo 等規(guī)則擴(kuò)展存儲(chǔ)方式位迂。具體參考官方文檔:動(dòng)態(tài)規(guī)則擴(kuò)展
一個(gè)服務(wù)若要使用 Nacos 中的配置作為 sentinel 規(guī)則,除了 Nacos 的依賴外,還需要引入如下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.0</version>
</dependency>
<!-- 為解決 Caused by: java.lang.ClassNotFoundException: com.alibaba.csp.sentinel.log.CommandCenterLog 引入 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.0</version>
</dependency>
配置:
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: sentinel-config
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
nacos 中新建一個(gè) sentinel-config 配置文件掂林,類型是 json臣缀,內(nèi)容比如:
[
{
"resource": "helloTest",
"limitApp": "default",
"grade": 1,
"count": 3,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
啟動(dòng)后可以看到 sentinel 中出現(xiàn)了這個(gè)規(guī)則。在 nacos 中修改該規(guī)則泻帮,發(fā)現(xiàn) sentinel 那邊同步修改了(反之則不行)
FAQ —— sentinel 部署路徑的問(wèn)題
之前部署 sentinel 的時(shí)候精置,出現(xiàn)過(guò)這樣一個(gè)問(wèn)題:我配置了一個(gè)域名給微服務(wù)平臺(tái)使用,其中的每一個(gè)模板锣杂,如 gateway, zipkin脂倦,sentinel,都是用 nginx 配置的 xxx.com/gateway/xxx 這樣的請(qǐng)求格式來(lái)訪問(wèn)的元莫。
打開(kāi) xxx.com/sentinel赖阻, 是可以看到 dashboard 和每個(gè)服務(wù)的,但是點(diǎn)開(kāi)服務(wù)踱蠢,發(fā)現(xiàn)監(jiān)控?cái)?shù)據(jù)沒(méi)有了火欧,簇點(diǎn)鏈路中每個(gè)地址的 QPS 都是0,點(diǎn)擊新增流控規(guī)則茎截,控制臺(tái)報(bào)了 404 異常苇侵。
在官方倉(cāng)庫(kù)提了這個(gè) issue,有位j叫 jasonjoo2010 的老哥給我解答了一下:
目前dashboard的靜態(tài)部分企锌,并不支持任意子目錄部署的方式榆浓,建議在不改動(dòng)的情況下,使用獨(dú)立域名部署撕攒。
后來(lái)我新增了一個(gè)二級(jí)域名來(lái)專門(mén)訪問(wèn) sentinel哀军,就可以了。
關(guān)于這個(gè)問(wèn)題的詳細(xì)描述在 sentinel 官方倉(cāng)庫(kù)中:https://github.com/alibaba/Sentinel/issues/1804
參考:
- Sentinel 是什么打却?—— 官方文檔
- 新手指南 —— 官方文檔
- https://blog.csdn.net/xiongxianze/article/details/87570156
- https://blog.csdn.net/weixin_44757206/article/details/107119085
最近在系統(tǒng)地學(xué)習(xí) Redis杉适、RabbitMQ、ES 等技術(shù)的知識(shí)柳击,著重關(guān)注原理猿推、底層、并發(fā)等問(wèn)題捌肴,關(guān)于相關(guān)技術(shù)分享后續(xù)會(huì)逐漸發(fā)布出來(lái)蹬叭。歡迎關(guān)注公眾號(hào):猿生物語(yǔ)(ID:JavaApes)