SpringCloud Alibaba Sentinel實作熔斷與限流
Sentinel概述
隨著微服務的流行,服務和服務之間的穩定性變得越來越重要,Sentinel 是面向分布式服務架構的流量控制組件,主要以流量為切入點,從限流、流量整形、熔斷降級、系統負載保護、熱點防護等多個維度來幫助開發者保障微服務的穩定性,

Sentinel下載地址
安裝Sentinel控制臺
Sentinel組件由倆部分組成:后臺和前臺8080,
- 核心庫(Java客戶端)不依賴任何框架/庫,能夠運行于所有Java運行時環境,同時對Dubbo/SpringCloud等框架也有較好的支持,
- 控制臺(Dashboard)基于Spring Boot開發,打包后可以直接運行, 不需要額外的Tomcat等應用容器,
下載到本地sentinel-dashboard-1.7.0.jar
運行命令:
- 前提:java8環境OK,8080埠不能被占用,
- 命令:java -jar sentinel-dashboard-1.7.0.jar
訪問sentinel管理界面:
- http://localhost:8080
- 賬號密碼均為sentinel
微服務整合sentinel
1.啟動Nacos
2.新建cloudalibaba-sentinel-service8401 Module
2.1 POM.xml
<dependencies>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</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>
2.2application.yml
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默認8719,假如被占用了會自動從8719開始依次+1掃描,直至找到未被占用的埠
management:
endpoints:
web:
exposure:
include: '*'
2.3主啟動類
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class,args);
}
}
2.4controller
@RestController
public class FlowLimitController
{
@GetMapping("/testA")
public String testA() {
return "------testA";
}
@GetMapping("/testB")
public String testB() {
return "------testB";
}
}
3.啟動Sentinel8080 啟動微服務8401
4.查看sentinel控制臺,發現空空如也,啥都沒有,是因為sentinel是采用懶加載,需要我們執行一次訪問,才會有資訊,
訪問localhost/8401/testA

可以看到,已經開始監聽了,
流控規則
基本介紹

- 資源名:唯一名稱,默認請求路徑,
- 針對來源:Sentinel可以針對呼叫者進行限流,填寫微服務名,默認default(不區分來源)
- 閾值型別/單機閾值:
- QPS(每秒鐘的請求數量):當呼叫該api的QPS達到閾值的時候,進行限流,
- 執行緒數:當呼叫該api的執行緒數達到閾值的時候,進行限流,
- 流控模式:
- 直接:api達到限流條件時,直接限流,
- 關聯:當關聯的資源達到閾值時,就限流自己,
- 鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進行的流量,如果達到閾值,就進行限流)【api級別的針對來源】
- 流控效果:
- 快速失敗:直接失敗,拋例外,
- Warm UP:根據codeFactor(冷加載因子,默認3)的值,從閾值/codeFactor,經過預熱時長,才達到設定的QPS閾值,
- 排隊等待:勻速排隊,讓請求以勻速的速度通過,閾值型別必須設定為QPS,否則無效,
流控模式
直接->快速失敗

快速點擊訪問http://localhost:8401/testA,直接失敗的效果如下圖:

執行緒數:

比如a請求過來,處理的慢,在一直處理,此時b請求又過來了,此時因為a占用一個執行緒,此時要處理b請求就只有額外開啟一個執行緒,那么就會報錯,
修改controller中testA方法,使用定時器來模擬業務處理請求比較慢的情況,
@GetMapping("/testA")
public String testA() {
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "------testA";
}

關聯
當關聯的資源達到閾值時,就限流自己,
一句話來說,當與A關聯的資源B達到閾值后,就限流自己,B惹事,A掛了,
應用場景:比如支付介面達到閾值時,就要限流下訂單的介面,防止一直有訂單,

這里可以使用postman模擬并發密集訪問testB,大批量執行緒高并發訪問B,導致A失效了, 這時候再去訪問testA,可以發現testA掛了,
鏈路
多個請求呼叫了同一個微服務,
流控效果
直接->快速失敗(默認的流控處理)
直接失敗,拋出例外
Blocked by Sentinel (flow limiting)
原始碼:
com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
預熱
公式:閾值除以coldFactor(默認值為3),經過預熱時長后才會達到閾值,

Warmup配置:

應用場景:秒殺系統在開啟的瞬間,會有很多流量上來,很有可能吧系統打死,預熱方式就是為了保護系統,可慢慢的把流量放進來,慢慢的把閾值增長到設定的閾值,
排隊等待:


降級規則
基本介紹
就是熔斷降級,


Sentinel熔斷降級會在呼叫鏈路中某個資源出現不穩定狀態時(例如呼叫超時或例外比例升高),對這個資源的呼叫進行限制,讓請求快速失敗,避免影響到其它的資源而導致級聯錯誤,
當資源被降級后,在接下來的降級時間視窗之內,對該資源的呼叫都自動熔斷(默認行為是拋出DegradeException),
Sentinel的斷路器是沒有半開狀態的,
半開的狀態系統自動去檢測是否請求有例外,沒有例外就關閉斷路器恢復使用,有例外則繼續打開斷路器不可用,具體可以 參考Hystrix,
降級策略
RT


測驗:
1.在controller添加一個方法,用于測驗
@GetMapping("/testD")
public String testD()
{
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
log.info("testD 測驗RT");
return "------testD";
}
2.配置RT,這里的配置的RT,默認是秒級的平均回應時間,

默認計算平均時間是,1秒內進入5個請求,并且回應的平均值超過閾值(這里設定的200ms),就報錯, 1秒5請求是Sentinel默認設定的,
測驗:

后續停止jmeter,沒有這么大的訪問量了,斷路器關閉(保險絲恢復),微服務恢復OK,
例外比例


測驗:
1.修改請求方法:
@GetMapping("/testD")
public String testD()
{
// try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
// log.info("testD 測驗RT");
log.info("testD 測驗RT");
int age = 10/0;
return "------testD";
}
2.配置

上面配置一句話來說,當1秒請求量>5,并且請求次數中出現例外的次數/總請求次數>0.2(我們設定的例外比例),就會出現服務熔斷,
3.jmeter

4.結論:
按照上述配置,單獨訪問一次,必然來一次報錯一次(int age = 10/0),調一次錯一次,因為沒有達到每秒的請求量>5,

開啟jmeter后,直接高并發發送請求,多次呼叫達到我們的配置條件了,
斷路器開啟(保險絲跳閘),微服務不可用了,不再報錯error而是服務降級了,

例外數


注意:例外數是按照分鐘統計的,
一分鐘內,有5個請求發生例外,進入熔斷,
測驗:
1.添加請求方法
@GetMapping("/testE")
public String testE()
{
log.info("testE 測驗例外數");
int age = 10/0;
return "------testE 測驗例外數";
}
2.配置

熱點key限流
何為熱點?熱點即經常訪問的資料,很多時候我們希望統計某個熱點資料中訪問頻次最高的Top K資料,并對其訪問進行限制,比如:
- 商品ID為引數,統計一段時間內最常購買的商品ID并進行限制,
- 用戶ID為引數,針對一段時間內頻繁訪問的用戶ID進行限制,
熱點引數限流會統計傳入引數中的熱點引數,并根據配置的限流閾值與模式,對包含熱點引數的資源呼叫進行限流,熱點引數限流可以看做是一種特殊的流量控制,僅對包含熱點引數的資源呼叫生效,
比如:
- localhost:8080/aa?name=aa
- localhost:8080/aa?name=bb
- 這兩個請求中,帶有引數aa的請求訪問頻次非常高,我們就限制name=aa的請求,但是bb的不限制,
如何自定義降級方法,而不是默認的拋出例外?

使用@SentinelResource直接實作降級方法,它等同Hystrix的@HystrixCommand,
@GetMapping("/testHotKey")
//定義一個名字 //指定降級方法
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2) {
//int age = 10/0;
return "------testHotKey";
}
//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception) {
return "------deal_testHotKey";
}
定義熱點規則:

比如說:我們controller中,有兩個引數p1,p2,這個引數索引0表示 是對p1進行限制,1表示對p2進行限制,以此類推,
測驗:
此時我們訪問/testHotkey并且帶上p1,如果qps大于1,就會觸發我們定義的降級方法,

如果我們的引數是p2,就沒有問題,

如果修改controller為@SentinelResource(value = “testHotKey”),不指定降級方法,那么例外會打到前臺用戶界面,不太友好,
只有帶上p1,才會觸發熱點限流,

設定熱點規則的其他選項
需求:

添加配置:

測驗:

注意:
引數型別只支持8種基本型別+String型別,
注意:
如果我們程式出現例外,是不會走blockHander的降級方法的,因為這個方法只配置了熱點規程,沒有配置限流規則,
我們這里配置的降級方法是sentinel針對熱點規則配置的,只有觸發熱點規則才會降級,

系統規則
系統自適應限流:從整體維度對應用入口進行限流,
對整體限流,比如qps到達100,這里限流會限制整個系統不可用,


測驗:

這里設定了,如果QPS到達1,那么整個系統將不可用,

@SentinelResource
用于配置降級等功能,
按資源名稱限流
1.啟動Nacos+Sentinel成功,
2.為8401添加依賴,添加我們自己的common包的依賴
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
3.額外創建一個controller類
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200, "按資源名稱限流測驗OK", new Payment(2020L, "serial001"));
}
public CommonResult handleException(BlockException exception) {
return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服務不可用");
}
}
3.配置限流
注意,我們這里配置規則,資源名指定的是@SentinelResource注解value的值,這樣也是可以的,也就是不一定要指定訪問路徑,

4.測驗,可以看到已經進入降級方法了

5.此時,我們關閉8401服務,可以看到,這些定義的規則是臨時的,關閉服務,規則就沒有了,

按照Url地址限流
通過訪問的URL來限流,會回傳Sentinel自帶默認的限流處理資訊
1.controller中添加一個方法,用于測驗
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
return new CommonResult(200,"按url限流測驗OK",new Payment(2020L,"serial002"));
}
2.Sentinel控制臺配置

3.測驗 回傳Sentinel自帶默認的限流處理資訊

上面兜底方法面臨的問題
- 系統默認的,沒有體現我們自己的業務要求,
- 依照現有條件,我們自定義的處理方法又和業務代碼耦合在一塊,不直觀,
- 每個業務方法都對應一個兜底的方法,代碼膨脹加劇,
- 全域統一的處理方法沒有體現,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/247642.html
標籤:其他
