git地址: https://github.com/alibaba/Sentinel
中文文檔: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
sentinel實際和hystrix的作用一樣,實現服務降級、熔斷等。但是hystrix的不足之處大概有:1.需要程序員手工搭建監控平台;2.沒有一套web界面可以給我們進行細粒度化的配置。Sentinel也是實現流量控制、速率控制、服務熔斷、服務降級。Sentinel有的優點如下:1.單獨的組件,可以獨立出來。2.直接界面化的細粒度統一配置。
1.下載安裝
1.下載jar包
到git https://github.com/alibaba/Sentinel/releases 下載即可。例如我下載的是:
sentinel-dashboard-1.8.0.jar
2. 啟動
直接以jar包的方式運行即可。
java -jar ./sentinel-dashboard-1.8.0.jar
3.訪問即可
默認的端口是8080,啟動后訪問,登錄: sentinel/sentinel
2. 新建項目使用sentinel初始化監控
新建一個項目,注冊中心為nacos,端口8848; 熔斷流量監控使用sentinel,端口8080.
1.新建項目cloudalibaba-sentinel-service8401
2.修改pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-sentinel-service8401</artifactId> <dependencies> <!--引入自己抽取的工具包--> <dependency> <groupId>cn.qz.cloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--SpringCloud ailibaba nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--SpringCloud ailibaba sentinel-datasource-nacos 后續做持久化用到--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <!--SpringCloud ailibaba sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- SpringBoot整合Web組件+actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--日常通用jar包配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
3.yml配置
server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 #Nacos服務注冊中心地址 sentinel: transport: dashboard: localhost:8080 #配置Sentinel dashboard地址 # 默認就是8719端口,如果占用會依次+1開始掃描 port: 8719 management: endpoints: web: exposure: include: '*'
4.主啟動類
package cn.qz.cloud.alibaba; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @Author: qlq * @Description * @Date: 22:06 2020/11/25 */ @EnableDiscoveryClient @SpringBootApplication public class MainApp8401 { public static void main(String[] args) { SpringApplication.run(MainApp8401.class, args); } }
5.測試Controller
package cn.qz.cloud.alibaba.controller; import cn.qz.cloud.utils.JSONResultUtil; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author: qlq * @Description * @Date: 22:07 2020/11/25 */ @RestController public class FlowLimitController { @GetMapping("/testA") public JSONResultUtil<String> testA() { return JSONResultUtil.successWithData("testA"); } @GetMapping("/testB") public JSONResultUtil<String> testB() { return JSONResultUtil.successWithData("testB"); } }
6.啟動后到nacos查看服務列表
7. 訪問sentinel
需要注意sentinel采用的是懶加載,如果直接啟動服務沒有訪問,sentinel是不會攔截到請求的。所以需要訪問幾次服務然后到sentinel中查看。sentinel控制台如下:
3. sentinel流控
1.實時監控
可以實時的查看每個請求的訪問QPS情況。
2.簇點鏈路
和上面1差不多。展示最近的訪問情況。
3.流控規則
新增流控規則的時候,界面如下:
概念性問題:
- 資源名:唯一名稱,默認請求路徑
- 針對來源:Sentinel可以針對調用者進行限流,填寫微服務名,默認default(不區分來源)
- 閾值類型/單機閾值
(1)QPS(每秒鍾的請求數):當掉該api達到閾值的時候,進行限流
(2)線程數: 當調用該API的線程數達到閾值時,進行限流
- 是否集群:不需要集群
- 流控模式
(1)直接:直接限流
(2)關聯:當關聯的資源達到閾值時,限流自己
(3)鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閾值,就進行限流)【api級別的針對來源】
- 流控效果
(1)快速失敗:直接失敗,拋異常
(2)Warm up:根據code Factor(冷加載印子,默認3)的值,從閾值/cedeFactor,經過預熱時長,才達到預設的QPS
(3)排隊等待: 勻速排隊,讓請求勻速的通過,閾值類型必須設置QPS。否則無效。
4. 流控模式使用
1. QPS 快速失敗
多次訪問查看結果如下:
2. 線程數直接失敗
這個是接收請求,但是當處理的線程數超過1的時候報上面的【Blocked by Sentinel (flow limiting)】。和上面不同的是這個允許請求進去,上面是達到閾值就不讓請求進去。
3.關聯流控
關聯流控就是關聯的資源達到閾值,限流自己。比如如下:當B超過QPS為1之后,A限流:
測試方法:
(1) 使用postman或者jmeter並發訪問testB,如下:加到collections中,然后沒300ms訪問一次,訪問30次
(2)訪問A發現A阻塞
4. 鏈路流控規則
(1)查看簇點鏈路,/testA的資源入口是sentinel_web_servlet_context
(2) 對A進行鏈路流控
(3)頻繁訪問會報Blocked by Sentinel (flow limiting)
5. 流控效果
1.直接快速失敗
默認的就是快速失敗,拋出阻塞信息。實現類是 com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
2.warm up預熱: com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController.WarmUpController
根據code Factor(冷加載印子,默認3)的值,從閾值/cedeFactor,經過預熱時長,才達到預設的QPS。比如下面規則表示:初始的時候閾值是10/3 = 3 ;5s的預熱時間達到10。
這種模式的應用場景是秒殺系統,開啟瞬間會有很多流量上來,很有可能把系統打死,預熱就是為了保護系統,讓流量緩慢的進來。
3. 排隊等待
勻速排隊,讓請求勻速的通過。這種很好理解,就是每秒鍾放行多少個請求。如下:每秒鍾處理1個,超過的話進行排隊;排隊時長超過2s報阻塞錯誤。源碼是com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。采用的算法是令牌桶算法。
4.sentinel熔斷降級
參考: https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
提供以下幾種熔斷策略:
(1)慢調用比例 (SLOW_REQUEST_RATIO):選擇以慢調用比例作為閾值,需要設置允許的慢調用 RT(即最大的響應時間),請求的響應時間大於該值則統計為慢調用。當單位統計時長(statIntervalMs)內請求數目大於設置的最小請求數目,並且慢調用的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求響應時間小於設置的慢調用 RT 則結束熔斷,若大於設置的慢調用 RT 則會再次被熔斷。
(2)異常比例 (ERROR_RATIO):當單位統計時長(statIntervalMs)內請求數目大於設置的最小請求數目,並且異常的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。異常比率的閾值范圍是 [0.0, 1.0],代表 0% - 100%。
(3)異常數 (ERROR_COUNT):當單位統計時長內的異常數目超過閾值之后會自動進行熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。
1. 慢調用比例模式
滿足兩個條件會觸發熔斷:
1》單位統計時長請求數大於設置的最小值。下面是每秒鍾5個。
2》慢請求達到設置的比例。
(1)新增方法
@GetMapping("/testD") public JSONResultUtil<String> testD() throws InterruptedException { TimeUnit.SECONDS.sleep(1); return JSONResultUtil.successWithData("testD"); }
(2)jmeter 以每秒鍾10次請求訪問
(3)curl訪問
$ curl http://localhost:8401/testD % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 35 0 35 0 0 249 0 --:--:-- --:--:-- --:--:-- 280Blocked by Sentinel (flow limiting)
(4)上面jmeter每秒鍾10次請求大於最小值5;每個請求處理時長超過200ms,滿足第二個條件,因此觸發降級。
2. 異常比例模式
如下:這個也是需要兩個條件。
1》每秒鍾請求超過五個
2》異常比例超過50%觸發熔斷
(1)新增方法
@GetMapping("/testE") public JSONResultUtil<String> testE() { int i = 1 / 0; return JSONResultUtil.successWithData("testE"); }
(2)jmeter每秒鍾十個訪問
(3)curl訪問:發現觸發降級
$ curl http://localhost:8401/testE % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 35 0 35 0 0 140 0 --:--:-- --:--:-- --:--:-- 149Blocked by Sentinel (flow limiting)
(4)停掉jmeter再次curl,直接爆程序錯誤。也就是沒觸發sentinel熔斷
$ curl http://localhost:8401/testE % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0{"timestamp":"2020-12-01T14:41:15.196+0000","status":500,"error":"Internal Server Error","message":"/ by zero","trace":"java.lang.ArithmeticException: / by zero\r\n\tat cn.qz.cloud.alibaba.controller.FlowLimitController.testE(FlowLimitController.java:43)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\r\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.lang.reflect.Method.invoke(Method.java:498)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)\r\n\tat org.springframework.web.servl
3.異常數模式
和上面的區別是根據異常數判斷是否需要進行熔斷。
5. 熱點規則
參考:https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
這個和hystrix挺像的。@SentinelResource代替@HystrixCommand。
1.新增方法
@GetMapping("/testHotKey") @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey") public JSONResultUtil<String> testHotKey(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2) { return JSONResultUtil.successWithData("testHotKey"); } public JSONResultUtil<String> deal_testHotKey(String p1, String p2, BlockException exception) { return JSONResultUtil.successWithData("deal_testHotKey"); //sentinel系統默認的提示:Blocked by Sentinel (flow limiting) }
解釋:
@SentinelResource注解的value是便於唯一標識,在后面sentinel限流配置的時候可以使用 /testHotKey ,也可以使用 testHotKey唯一標識
blockHandler 類似於hystrixCommand注解的兜底方法。
2.sentinel增加如下配置:
上面的配置規則是:
(1)如果 testHotKey 方法不帶p1 參數不進行限流
(2)如果待了p1參數且值不為"1",閾值為2;如果值為"1", 閾值可達到20。 (這里需要注意例外項的參數類型只支持8種基本數據類型和String類型)
(3)p2參數不參數限流
6. 系統規則
系統保護規則是從應用級別的入口流量進行控制,從單台機器的 load、CPU 使用率、平均 RT、入口 QPS 和並發線程數等幾個維度監控應用指標,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。
- Load 自適應(僅對 Linux/Unix-like 機器生效):系統的 load1 作為啟發指標,進行自適應系統保護。當系統 load1 超過設定的啟發值,且系統當前的並發線程數超過估算的系統容量時才會觸發系統保護(BBR 階段)。系統容量由系統的
maxQps * minRt
估算得出。設定參考值一般是CPU cores * 2.5
。 - CPU usage(1.5.0+ 版本):當系統 CPU 使用率超過閾值即觸發系統保護(取值范圍 0.0-1.0),比較靈敏。
- 平均 RT:當單台機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。
- 並發線程數:當單台機器上所有入口流量的並發線程數達到閾值即觸發系統保護。
- 入口 QPS:當單台機器上所有入口流量的 QPS 達到閾值即觸發系統保護。
可以理解為系統規則是全局的限流配置,可以針對服務的全局QPS、機器的CPU等參數進行限流。
7.@SentinelResource
這個注解類似於HystrixCommand注解,可以用於方法的降級處理。
1.常規用法
package cn.qz.cloud.alibaba.controller; import cn.qz.cloud.utils.JSONResultUtil; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class RateLimitController { @GetMapping("/byResource") @SentinelResource(value = "byResource", blockHandler = "handleException") public JSONResultUtil<String> byResource() { return JSONResultUtil.successWithData("byResource"); } public JSONResultUtil<String> handleException(BlockException exception) { return JSONResultUtil.successWithData("handleException"); } @GetMapping("/rateLimit/byUrl") @SentinelResource(value = "byUrl") public JSONResultUtil<String> byUrl() { return JSONResultUtil.successWithData("byUrl"); } }
(1)新建兩條限流規則:
第一條: 資源名是@SentinelResource 的value屬性
第二條: 資源名是URL,等價於上面的@SentinelResource的value屬性
(2)測試:
1》 byResource 超出會走自己指定的方法
2》byUrl 超出后走默認的限流方法
(3)存在的問題:
1》系統默認限流,滿足不了業務要求。
2》自定義的處理方法和業務代碼耦合在一起,不直觀。
3》每個方法都添加一個兜底的,代碼膨脹加劇。
4》全局統一的處理方法沒有體現。
2. 自定義異常處理器
(1)新建異常處理器
package cn.qz.cloud.alibaba.myhandler; import cn.qz.cloud.utils.JSONResultUtil; import com.alibaba.csp.sentinel.slots.block.BlockException; public class CustomerBlockHandler { public static JSONResultUtil<Object> handlerException(BlockException exception) { return JSONResultUtil.errorWithMsg("-1", "error"); } public static JSONResultUtil<Object> handlerException2(BlockException exception) { return JSONResultUtil.errorWithMsg("-1", "error2"); } }
(2)Controller使用@SentinelResource注解指明異常處理器以及方法
@GetMapping("/rateLimit/cusError") @SentinelResource(value = "cusError", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2") public JSONResultUtil<String> cusError() { return JSONResultUtil.successWithData("cusError"); }
(3)新建流控
(3) 測試超出限制
8.@SentnelResource 補充
主要屬性 fallback和blockHandler。fallback管運行異常,blockHandler管配置違規,包括流量超限等。IllegalArgumentException 可以指定忽略的異常。例如:
package cn.qz.cloud.alibaba.controller; import cn.qz.cloud.bean.Payment; import cn.qz.cloud.utils.JSONResultUtil; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; @RestController @Slf4j public class CircleBreakerController { public static final String SERVICE_URL = "http://nacos-payment-provider"; @Resource private RestTemplate restTemplate; @RequestMapping("/consumer/fallback/{id}") //@SentinelResource(value = "fallback") //沒有配置 //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只負責業務異常 //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只負責sentinel控制台配置違規 @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler", exceptionsToIgnore = {IllegalArgumentException.class}) public JSONResultUtil<Payment> fallback(@PathVariable Long id) { JSONResultUtil<Payment> result = restTemplate.getForObject(SERVICE_URL + "/list/" + id, JSONResultUtil.class, id); if (id == 4) { throw new IllegalArgumentException("IllegalArgumentException,非法參數異常...."); } else if (result.getData() == null) { throw new NullPointerException("NullPointerException,該ID沒有對應記錄,空指針異常"); } return result; } //本例是fallback public JSONResultUtil<Payment> handlerFallback(@PathVariable Long id, Throwable e) { JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null); objectJSONResultUtil.setMsg("handlerFallback: " + e); return objectJSONResultUtil; } //本例是blockHandler public JSONResultUtil<Payment> blockHandler(@PathVariable Long id, BlockException blockException) { JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null); objectJSONResultUtil.setMsg("blockHandler : " + blockException); return objectJSONResultUtil; } }
nacos-payment-provider服務只有1、2、3 ID返回數據。測試:
(1)ID為4拋出非法參數異常,這種異常exceptionsToIgnore 忽略兜底
(2)ID為4以后的走fallback
9.sentinel持久化
持久化需要借助nacos,就相當於配置信息存到nacos,然后后nacos讀取配置。
1.應用的yml增加如下配置:
spring: application: name: nacos-order-consumer cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: #配置Sentinel dashboard地址 dashboard: localhost:8080 #默認8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口 port: 8719 datasource: ds1: nacos: server-addr: localhost:8848 dataId: nacos-order-consumer groupId: DEFAULT_GROUP data-type: json rule-type: flow
2.到nacos新建配置
這里需要注意,data Id 和 上面應用中配置的一樣。
內容如下:
[
{
"resource": "/consumer/fallback/2",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
解釋:
resource:資源名稱
limitApp:來源應用
grade:閾值類型,0表示線程數,1表示QPS
count:單擊閾值
strategy:流控模式,0表示直接,1表示關聯,2表示鏈路
controlBehavior:流控效果,0表示快速失敗,1表示Warm up,2表示排隊等待