文章目錄
特別聲明:整理自慕課網大目師兄的微服務視頻,鏈接:https://coding.imooc.com/learn/list/358.html
前情提要:在nacos上註冊了content-center和user-center兩個服務,content-center使用Feign調用user-center服務,使用Ribbon做負載均衡,sentinel實現服務容錯!sentinel使用簡單,精通就難了,可以配置的選擇很多,可以操作的餘地也很足!
1.爲什麼需要服務容錯?
1.1 雪崩效應
雪崩效應(級聯故障):如圖所示,C,D服務調用B服務,B服務調用A服務,如果A服務突然發生故障,那麼B服務調用A服務就得不到返回,直到請求超時,在超時之前的那段時間內,請求一直在等待,簡直欲穿秋水,一個請求就是一個線程,線程一直處於阻塞狀態,會一直佔用服務器的資源,比如內存,cpu;如果B的併發很高,這樣阻塞的線程很多,那麼B服務器再也沒有資源去創建新的線程,於是B也掛了,然後C,D服務又請求不到B服務了…然後C,D服務也掛了。因爲A服務的故障,BCD沒有做任何處理,都掛了,這就是雪崩效應
1.2 什麼是服務容錯
我的理解就是服務容錯的目的就是保護自己服務的正常運行。比如B服務碰到上面那種請求等待導致線程太多的時候,我就限流,某某請求的線程不得超過10個,超過10個就不再接受新的請求,直接返回被限流錯誤提示,這樣B服務就不會因爲太多線程耗盡內存而亡了
1.3 常見的容錯方案
- 超時
最容易想到的方案,把請求時間設置的很短,這樣線程釋放的足夠快,B服務就不會那麼容易被耗死了
體現思想:天下武功,唯快不破! - 限流
我們經過測試,發現某個請求能承受的最大QPS(每秒查詢率)是1000,那我們就把這個請求的QPS閾值設置成800,超過了這個閾值,請求直接返回被限流的錯誤(限流功能僅僅是Setinel衆多功能當中的一個)
體現思想:世相萬變,我心不變,他弱由他弱,清風拂山崗,他強由他強,明月照大江! - 倉壁模式
創造很多的隔斷間,每個隔斷間都有一個自己的線程池,你自己的線程池滿了,後續請求按照線程池的規則排隊,還是啥的就行了,不會影響到其它的隔斷間。可以把Controller當作隔斷間,也可以是其它的,反正就是這個意思
體現思想:不把雞蛋放一個籃子裏! - 斷路器模式
檢測一定時間內的錯誤率,錯誤數,比如5秒之內錯誤率,錯誤次數,達到某個閾值,就認爲B服務所調用的A服務掛了,我就跳閘,就不會堆積那麼多線程等待了,然後在10秒之後(稱作斷路器時間窗口),斷路器變成半開狀態,此狀態向A再發送僅僅一次請求,如果這次請求又失敗了,我再跳閘,默默的等10秒(斷路器時間窗口,不是非得10秒,我只是舉例子,值自己可以設定),斷路器又變成半開狀態,再次向A發送一次請求,如果這次請求成功了,斷路器就徹底恢復,閘徹底閉合(斷路器模式也是Setinel衆多功能當中的一個)
體現思想:可以自我修復的高級保險絲!
2.部署Sentinel控制檯
Sentinel和nacos有點像,都有個控制檯
2.1 下載Sentinel.jar包
- 下載地址
https://github.com/alibaba/Sentinel/releases,我們用的spring-cloud-aibaba是0.9版本的,生產上我們應該下載同版本的Sentinel.jar。學習測試就選最新的V1.6.3,點擊下載
2.2 運行Sentinel.jar包
Sentinel的端口是8080,所以你得把8080端口先空出來
一般執行下面這個命令就行了,不過窗口關閉,服務也會停掉
java -jar sentinel-dashboard-1.6.3.jar
這個命令是linux系統後臺窗口運行的,窗口關閉,服務也不會停止
nohup java -jar sentinel-dashboard-1.6.3.jar &
2.3 登錄Sentinel控制檯
ip+端口號訪問服務,用戶名和密碼都是:sentinel,我用本地的sentinel
3.微服務整合Sentinel
依舊三板斧
3.1 加依賴
sentinel建立在springboot的actuator組件上
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3.2 寫註解
暫無註解
3.3 寫配置
指定sentinel 控制檯地址
spring:
cloud:
sentinel:
transport:
# 指定 sentinel 控制檯地址
dashboard: localhost:8080
management:
endpoints:
web:
exposure:
# 暴露出所有actuator監控的端點
include: '*'
3.4 懶加載
sentinel是懶加載的,所以得訪問一下content-center服務的http://localhost:8081/poem/testRibbon接口,再刷新Sentinel控制檯,就能看到content-center了
4.簇點鏈路
- 給資源添加規則的入口
- 被請求的路徑,都稱作資源,會在簇點鏈路裏顯示出來,沒有被請求的就不會顯示出來
5.流控規則限流
5.1 參數解釋
參數名 | 含義 |
---|---|
資源名 | 即限流規則的作用對象 |
針對來源 | 流控針對的調用來源(服務級別),若爲 default 則不區分調用來源 |
閾值類型 | QPS(每秒查詢率) 或併發線程數(感覺這個就是倉壁模式) |
單機閾值 | 限流閾值,超過這個值就執行流控規則 |
流控模式 | 直接,關聯,鏈路 |
流控效果 | 直接拒絕、Warm Up、勻速排隊 |
是否集羣 | 這個功能好像還不能用 |
比較難以理解的就是流控模式和流控效果
5.1.1 流控模式
基於調用關係的流量控制
- 直接
直接針對資源本身 - 關聯
當兩個資源之間具有資源爭搶或者依賴關係的時候,這兩個資源便具有了關聯。當關聯的資源達到閾值,就限流自己,讓自己讓出系統資源給關聯的資源,也就是說在限流自己保護關聯資源 - 鏈路
資源通過調用關係,相互之間構成一棵調用樹,老抽象了,下面的common和chain/b就是樹的關係,這就是一條鏈路
5.1.2 流控效果
- 直接拒絕
當QPS或線程數超過規則的閾值後,新的請求就會被立即拒絕,拒絕方式爲拋出FlowException。這種方式適用於對系統處理能力確切已知的情況下,比如通過壓測確定了系統的準確水位時 - Warm Up
當流量突然增加時,直接把系統拉昇到高水位可能瞬間把系統壓垮。通過"冷啓動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮 - 勻速排隊
嚴格控制請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應的是漏桶算法。這種方式主要用於處理間隔性突發的流量,例如消息隊列。想象一下這樣的場景,在某一秒有大量的請求到來,而接下來的幾秒則處於空閒狀態,我們希望系統能夠在接下來的空閒期間逐漸處理這些請求,而不是在第一秒直接拒絕多餘的請求
5.2 測試三種流控模式
5.2.1 直接
默認就是直接
- 添加規則
對資源testSentinel/QPSFlowControl添加如下流控規則:如果QPS(每秒查詢率)閾值超過1,就直接返回失敗
- 測試
快速多次刷新,可以看到,通過6次請求,拒絕14次,規則起作用了
5.2.2 關聯
- 寫了兩個請求
@RestController
@Slf4j
@RequestMapping("/testSentinel")
public class FlowControl {
@GetMapping("related/a")
public String flowRelatedA(){
log.info("我是被限流的資源......");
return "related/a";
}
@GetMapping("related/b")
public String flowRelatedB(){
log.info("我是被關聯資源,不是限流我......");
return "related/b";
}
- 配置/testSentinel/related/a的限流
如果/testSentinel/related/b的QPS達到1,那麼限流/testSentinel/related/a
- 使用Postman 25秒內訪問/testSentinel/related/b 50次,每500ms一次,QPS=2 > 閾值1,滿足限流條件
postman請求之前,可以正常訪問/testSentinel/related/a
啓動postman請求之後:
從控制檯可以看到/testSentinel/related/b 50次請求全通過,但是/testSentinel/related/a的請求就被拒絕了,測試通過
5.2.3 鏈路
- 測試代碼,兩個請求資源:/testSentinel/chain/a,/testSentinel/chain/b
flowService.common() 註解爲sentinel資源,a和b都使用了common方法
@RequestMapping("/testSentinel")
public class FlowController {
@Autowired
FlowService flowService;
@GetMapping("chain/a")
public String chainA(){
flowService.common();
return "chain/a";
}
@GetMapping("chain/b")
public String chainB(){
flowService.common();
return "chain/b";
}
@Service
@Slf4j
public class FlowService {
@SentinelResource
public void common(){
log.info("common......");
}
}
- 配置common的限流,鏈路入口資源/testSentinel/chain/a
如果/testSentinel/chain/a的QPS達到1,那麼限流/testSentinel/chain/a。而/testSentinel/chain/b毫無影響
- 測試
瘋狂刷新 http://localhost:8081/testSentinel/chain/a,被限流了
瘋狂刷新 http://localhost:8081/testSentinel/chain/b,毫無阻礙!符合鏈路規則
5.3 測試三種流控效果
5.3.1 快速失敗
上一節測試的默認都是這種流控效果,返回限流消息或者500錯誤
5.3.2 Warm Up
初始閾值爲設定閾值/codeFactor(默認3),經過預熱時長,閾值升高到設定閾值,讓可通過的流量緩慢增長
- 修改單擊閾值爲6,流控效果選擇 Warm Up
實際上初始閾值=6/3,爲2,經過10秒之後,閾值升高到6
- 測試
使用postman測試,0.1秒請求一次,總共請求200次,QPS爲10 ,持續20秒 http://localhost:8081/testSentinel/QPSFlowControl,
sentinel的實時監控臺
21:06:46秒時,通過QPS爲2,符合 6/3=2
21:06:58秒開始,通過QPS爲6,後面穩定6,直到QPS自己降下來,預熱時間
58-46=12秒,略大於設置的10秒。總體基本符合限流設置!
5.3.3 排隊等待
這個限流效果只對閾值類型QPS的起作用
- 設置/testSentinel/QPSFlowContro限流規則
設置之前清除它其它的容錯規則,以免被幹擾。設置閾值2,超時時間10秒,
規則效果:QPS超過閾值的請求,不放棄請求,而是讓請求排隊等待被執行,服務以一秒2次的速度處理排隊的請求,如果某個請求的等待時間超過10秒,纔會返回失敗
- 測試
使用postman以一秒10次的速度請求http://localhost:8081/testSentinel/QPSFlowControl 100次,可以看到QPS一直是2,100請求全部通過,沒有請求被丟棄,符合規則預期!
6.降級規則限流
6.1 三種降級策略
6.1.1 RT (Response Time)
6.1.1.1 釋義
當1s內持續進入5個請求,對應時刻的平均響應時間(秒級統計,單位毫秒)均超過閾值ms,斷路器打開,返回請求失敗,知道時間窗口設置的時間結束,關閉降級
- RT默認上限是4900 ms
如需改動,可以通過下面的配置項來設置
-Dcsp.sentinel.statistic.max.rt=xxx
6.1.1.2 測試
- 測試準備代碼
@RestController
@Slf4j
@RequestMapping("/down")
public class DownController {
@GetMapping("rt/a")
public String rtA(){
log.info("rtA....");
return "rt/a";
}
}
- 新增降級規則
1s內持續進入5個請求,平均響應時間 > 1ms,就返回錯誤提示,直到4秒之後恢復正常
- 瘋狂刷新 http://localhost:8081/down/rt/a,考驗手速的時候到了,RT肯定會大於1ms的,於是降級了
歇4秒,再次訪問,正常了
6.1.2 異常比率
6.1.2.1 釋義
當資源的每秒請求量 >= 5,並且每秒異常總數佔通過量的比值超過閾值,資源進入降級狀態;接下的時間窗口之後,降級恢復
異常比率的閾值範圍是 [0.0, 1.0],代表 0% - 100%
6.1.2.2 測試
- 寫個必拋異常的方法
要想異常被統計,必須 Tracer.trace(t)
@GetMapping("rt/b")
public String rtB() {
try {
log.info("rtB....");
throw new RuntimeException("throw runtime ");
} catch (Throwable t) {
Tracer.trace(t);
}
return "rt/b";
}
- 配置規則
資源的每秒請求量 >= 5,秒級異常比例 > 0.1,降級,4秒之後恢復
- 測試
瘋狂刷新 http://localhost:8081/down/rt/b,必報異常,所有異常比率 > 0.1
4秒之後在刷新:
6.1.3 異常數
6.1.3.1 釋義
資源近1分鐘的異常數目超過閾值之後會進行降級,統計時間窗口是分鐘級別的,所以時間窗口最好大於60s
6.1.3.2 測試
比較好理解,不測試了