在微服務架構里,服務間調用關系錯綜復雜,一個服務出問題很可能引發連鎖反應,也就是 “雪崩”。今天就帶大家從零開始學習 Sentinel,這款阿里開源的微服務保護工具,幫你解決雪崩難題,做好流量控制、隔離降級。
一、初識 Sentinel:先搞懂雪崩和解決方案
1.1 雪崩問題到底是啥?
微服務中,一個服務往往依賴多個其他服務。比如服務 A 依賴服務 B,服務 B 又依賴服務 C。要是服務 C 故障了,服務 B 調用 C 時會阻塞,服務 A 調用 B 也會跟著阻塞。
服務器的線程和并發數是有限的,一旦大量請求阻塞,服務器資源很快就會耗盡,進而導致所有服務不可用。接著,依賴這些服務的其他服務也會陸續 “掛掉”,形成級聯失敗 —— 這就是雪崩。
1.2 解決雪崩的 4 種核心方案
既然雪崩這么可怕,那怎么防?主要有 4 種思路:
- 超時處理:給請求設個超時時間,超過時間沒響應就返回錯誤,別一直等。比如調用一個服務,超過 200ms 就直接提示 “請求超時”,避免線程一直占用。
- 倉壁模式(線程隔離):像船艙的防水隔板一樣,給每個業務分配獨立的線程池。比如訂單業務用 10 個線程,商品業務用 10 個線程,就算訂單業務線程耗盡,也不影響商品業務。
- 斷路器:統計服務調用的異常比例,要是超過閾值(比如異常率 50%),就 “熔斷”—— 暫時攔截所有訪問該服務的請求,等服務恢復了再放行。
- 限流:限制服務的 QPS(每秒請求數),比如每秒最多處理 100 個請求,超過的直接拒絕,避免突發流量把服務沖垮。
簡單總結下:限流是 “預防”,提前擋住過多流量;超時、倉壁、斷路器是 “補救”,故障發生時控制影響范圍。
1.3 服務保護工具對比:為啥選 Sentinel?
Spring Cloud 里有 Hystrix、Resilience4J、Sentinel 等工具,國內用得最多的是 Sentinel,對比 Hystrix 優勢很明顯:
對比項 | Sentinel | Hystrix |
---|---|---|
隔離策略 | 信號量隔離 | 線程池 / 信號量隔離 |
熔斷策略 | 慢調用 / 異常比例 | 失敗比率 |
流量整形 | 支持預熱、排隊等待 | 不支持 |
控制臺 | 開箱即用,秒級監控 | 不完善 |
生態適配 | Spring Cloud、Dubbo 等 | 主要適配 Spring Cloud Netflix |
Sentinel 不僅功能全,還能承接阿里雙十一大促的流量,穩定性有保障,這也是咱們選它的核心原因。
1.4 Sentinel 安裝:3 步搞定
Sentinel 有個可視化控制臺,能配置規則、看監控,安裝特別簡單:
- 下載 jar 包:去 GitHub 搜 “Sentinel”,下載最新的 dashboard jar 包(比如 sentinel-dashboard-1.8.1.jar)。
- 運行 jar 包:把 jar 包放到非中文目錄,打開命令行執行:
# 默認端口8080,賬號密碼都是sentinel java -jar sentinel-dashboard-1.8.1.jar # 想改端口的話,比如改到8090 java -Dserver.port=8090 -jar sentinel-dashboard-1.8.1.jar
- 訪問控制臺:打開瀏覽器輸
http://localhost:8080
,輸入賬號密碼(都是 sentinel)就能進。剛進去是空白的,因為還沒整合微服務。
1.5 微服務整合 Sentinel:以 order-service 為例
以訂單服務(shop-order)為例,整合 Sentinel 就 3 步:
- 加依賴:在 pom.xml 里加 Sentinel 依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
- 配控制臺地址:在 application.yaml 里加配置,告訴服務控制臺在哪:
spring:cloud:sentinel:transport:dashboard: localhost:8080 # 控制臺地址
- 觸發監控:啟動服務后,訪問一個接口(比如
http://localhost:8091/order/prod/19
),再刷新控制臺,就能看到服務的監控數據了。
二、流量控制:提前擋住突發流量
限流是預防雪崩的關鍵,Sentinel 的流量控制功能很強大,咱們從基礎到進階慢慢講。
2.1 先認識 “簇點鏈路”
請求進微服務時,會走DispatcherServlet → Controller → Service → Mapper
這樣的調用鏈,這就是 “簇點鏈路”。鏈路上的每個接口(比如 Controller 的方法)都是一個 “資源”,Sentinel 默認會監控 SpringMVC 的所有接口。
在控制臺的 “簇點鏈路” 菜單里,能看到所有資源,每個資源后面都有 “流控”“降級” 等按鈕,點進去就能配置規則。
2.2 流控快速入門:給接口設 QPS 閾值
比如給/order/prod/{pid}
這個接口設限流規則:QPS 不超過 5(每秒最多 5 個請求)。
- 加規則:在簇點鏈路里,找到
/order/prod/{pid}
,點后面的 “流控”,配置:- 閾值類型:QPS
- 單機閾值:5
- 其他默認,點 “新增”。
- 用 Jmeter 測試:建個線程組,20 個用戶、2 秒內發完(QPS=10,超過 5)。運行后看結果,每秒只會成功 5 個請求,超過的會被拒絕。
2.3 3 種流控模式:應對不同場景
流控模式是指 “什么時候觸發限流”,Sentinel 支持 3 種,咱們結合場景講:
2.3.1 直接模式:默認模式,對當前資源限流
剛才的快速入門就是直接模式 —— 只要當前資源的 QPS 超過閾值,就直接限流。適合簡單的接口保護,比如普通查詢接口。
2.3.2 關聯模式:高優先級資源優先,限流低優先級
場景:比如訂單的 “更新”(/order/update)和 “查詢”(/order/query)都操作數據庫,更新要優先保證,要是更新請求太多,就限流查詢。
- 加接口:在 OrderController 里加兩個接口:
// 查詢訂單 @GetMapping("/order/query") public String queryOrder() {return "查詢訂單成功"; } // 更新訂單 @GetMapping("/order/update") public String updateOrder() {return "更新訂單成功"; }
- 配規則:給
/order/query
加關聯流控規則:- 流控模式:關聯
- 關聯資源:
/order/update
(當更新接口 QPS 超 5 時,限流查詢) - 閾值:5
- 測試:用 Jmeter 壓
/order/update
(QPS=10),再用瀏覽器訪問/order/query
,會提示 “Blocked by Sentinel (flow limiting)”—— 查詢被限流了。
2.3.3 鏈路模式:只限流指定來源的請求
場景:查詢訂單(/order/query)和創建訂單(/order/save)都要調用 “查詢商品”(queryGoods)方法,只想限流從 “查詢訂單” 進來的請求。
- 加方法和接口:
- OrderService 加 queryGoods 方法,用
@SentinelResource("goods")
標記(讓 Sentinel 監控):@SentinelResource("goods") public void queryGoods() {System.err.println("查詢商品"); }
- OrderController 加
/order/save
,并讓/order/query
和/order/save
都調用 queryGoods:// 查詢訂單調用商品查詢 @GetMapping("/order/query") public String queryOrder() {orderService.queryGoods();return "查詢訂單成功"; } // 新增訂單調用商品查詢 @GetMapping("/order/save") public String saveOrder() {orderService.queryGoods();return "新增訂單成功"; }
- OrderService 加 queryGoods 方法,用
- 關資源聚合:Sentinel 默認會把 SpringMVC 請求聚合到一個 root 資源,鏈路模式會失效,所以要在 application.yaml 里關了:
yaml
spring:cloud:sentinel:web-context-unify: false # 關閉context整合
- 配規則:給 “goods” 資源加鏈路流控規則:
- 流控模式:鏈路
- 入口資源:
/order/query
(只統計從查詢訂單進來的請求) - 閾值:2
- 測試:用 Jmeter 壓
/order/save
(QPS=4),訪問/order/save
正常;壓/order/query
(QPS=4),超過 2 的請求會被限流。
2.4 3 種流控效果:超過閾值后怎么處理?
流控效果是指 “QPS 超閾值后,怎么處理新請求”,Sentinel 支持 3 種:
2.4.1 warm up(預熱模式):應對服務冷啟動
服務剛啟動時,資源沒初始化(比如連接池沒建立),要是直接扛最大 QPS,可能會宕機。預熱模式就是讓閾值從一個小值慢慢漲到最大值,給服務 “預熱” 時間。
比如給/order/prod/{pid}
設規則:
- 閾值類型:QPS
- 單機閾值:10(最大 QPS)
- 流控效果:warm up
- 預熱時長:5 秒
Sentinel 默認冷啟動因子是 3,所以初始閾值是 10/3≈3。剛啟動時,每秒只能過 3 個請求,5 秒后慢慢漲到 10 個。用 Jmeter 測試,剛開始失敗率高,后來會越來越低。
2.4.2 排隊等待:讓請求平滑執行
快速失敗和 warm up 都是直接拒絕超閾值請求,排隊等待則是讓請求排隊,按固定間隔執行,避免流量波動。
比如給/order/prod/{pid}
設規則:
- 閾值類型:QPS
- 單機閾值:10(每秒 10 個,即每 200ms 一個)
- 流控效果:排隊等待
- 超時時間:5000ms(請求等待超過 5 秒就拒絕)
用 Jmeter 壓 QPS=15(超閾值),結果所有請求都會通過,但響應時間會變長 —— 因為都在排隊。控制臺看 QPS 曲線會很平滑,一直保持在 10 左右,對服務器很友好。
2.4.3 快速失敗:默認效果,直接拒絕
超過閾值后,新請求直接被拒絕,拋出FlowException
,適合對響應時間敏感的場景(比如支付接口,不能讓用戶等)。
2.5 熱點參數限流:對不同參數區別對待
之前的限流是統計整個資源的 QPS,熱點參數限流則是按 “參數值” 統計。比如/order/prod/{pid}
接口,pid=1 是秒殺商品(熱點),pid=2 是普通商品,想給它們設不同的 QPS 閾值。
2.5.1 實戰案例:給 pid 設不同閾值
需求:/order/prod/{pid}
默認 QPS=2,pid=1 設 QPS=4,pid=19 設 QPS=10。
- 標記資源:給接口加
@SentinelResource("hot")
(熱點限流要這個注解):@SentinelResource("hot") @GetMapping("/order/prod/{pid}") public String getOrderByPid(@PathVariable Integer pid) {// 業務邏輯 }
- 加熱點規則:控制臺進 “熱點規則”,點 “新增”:
- 資源名:hot
- 限流模式:QPS 模式
- 參數索引:0(第一個參數,即 pid)
- 單機閾值:2(默認閾值)
- 高級選項→參數例外項:
- 加 pid=1(long 類型),閾值 4
- 加 pid=19(long 類型),閾值 10
- 測試:用 Jmeter 分別壓 pid=1(QPS=5)、pid=19(QPS=12)、普通 pid(QPS=3):
- pid=1:每秒成功 4 個
- pid=19:每秒成功 10 個
- 普通 pid:每秒成功 2 個
三、隔離和降級:故障發生時控制影響范圍
限流是預防,隔離和降級是故障發生后的 “止損” 手段。Sentinel 主要通過線程隔離(倉壁模式)和熔斷降級來實現。
3.1 Feign 整合 Sentinel:遠程調用要保護
微服務遠程調用大多用 Feign,所以要先整合 Feign 和 Sentinel,才能在遠程調用時做隔離和降級。
3.1.1 開啟 Feign 的 Sentinel 支持
在 order-service 的 application.yaml 里加配置:
feign:sentinel:enabled: true # 開啟Feign對Sentinel的支持
3.1.2 寫失敗降級邏輯
遠程調用失敗時(比如服務宕機),不能直接報錯,要返回友好提示。有兩種方式:
方式 1:FallbackClass(不支持捕獲異常)
- 寫降級類,實現 Feign 接口:
@Component public class ProductServiceFallback implements IProductService {@Overridepublic Product findByPid(Integer pid) {// 降級邏輯:返回默認值Product product = new Product();product.setPid(-1);product.setPname("暫無商品");return product;} }
- Feign 接口指定降級類:
@FeignClient(value = "service-product", fallback = ProductServiceFallback.class) public interface IProductService {@GetMapping("/product/{pid}")Product findByPid(@PathVariable Integer pid); }
方式 2:FallbackFactory(支持捕獲異常)
要是想知道調用失敗的原因,就用 FallbackFactory:
- 寫降級工廠類:
@Component public class ProductServiceFallbackFactory implements FallbackFactory<IProductService> {@Overridepublic IProductService create(Throwable throwable) {// 打印異常信息System.out.println("調用失敗:" + throwable.getMessage());// 返回降級邏輯return new IProductService() {@Overridepublic Product findByPid(Integer pid) {Product product = new Product();product.setPid(-1);product.setPname("暫無商品");return product;}};} }
- Feign 接口指定降級工廠:
@FeignClient(value = "service-product", fallbackFactory = ProductServiceFallbackFactory.class) public interface IProductService {@GetMapping("/product/{pid}")Product findByPid(@PathVariable Integer pid); }
3.2 線程隔離(倉壁模式):避免資源耗盡
線程隔離是給每個遠程調用分配獨立的線程池(或信號量),就算某個調用故障,也只消耗該線程池的資源,不影響其他調用。
3.2.1 兩種隔離方式對比
隔離方式 | 原理 | 優點 | 缺點 |
---|---|---|---|
信號量隔離 | 計數器模式,統計線程數 | 簡單,開銷小 | 不能隔離慢調用 |
線程池隔離 | 給每個調用分配獨立線程池 | 隔離徹底,支持慢調用 | 線程切換開銷大 |
Sentinel 默認用信號量隔離,要是需要隔離慢調用(比如調用一個慢服務),就用線程池隔離。
3.2.2 實戰:給 Feign 接口設線程數閾值
需求:order-service 調用 product-service 的接口,線程數不超過 2(即同時最多 2 個線程調用)。
- 加規則:在簇點鏈路里找到 Feign 接口(比如
/product/{pid}
),點 “流控”:- 閾值類型:線程數
- 單機閾值:2
- 其他默認
- 測試:用 Jmeter 發 10 個并發請求(瞬間發完),會發現部分請求返回降級邏輯(“暫無商品”)—— 因為線程數超 2 了,超出的請求被限流。
3.3 熔斷降級:故障服務暫時 “隔離”
要是一個服務調用失敗率很高(比如 80% 的請求都失敗),繼續調用只會浪費資源,這時候就該 “熔斷”—— 暫時攔截所有調用,等服務恢復了再放行。
Sentinel 的熔斷策略有兩種:
- 慢調用比例:請求響應時間超過 “慢調用閾值” 的比例,超過閾值就熔斷。
- 異常比例:調用失敗(拋異常)的比例超過閾值,就熔斷。
配置方式和流控類似,在簇點鏈路里點 “降級”,選對應的策略和閾值即可。比如設 “異常比例”:異常率超過 50%,熔斷時長 5 秒 ——5 秒內所有調用都會被攔截,5 秒后會放少量請求試探,要是恢復了就取消熔斷。
總結
Sentinel 的核心功能就是 “流量控制” 和 “隔離降級”:
- 流量控制:通過直接 / 關聯 / 鏈路模式,結合快速失敗 /warm up / 排隊效果,提前擋住突發流量,預防雪崩。
- 隔離降級:通過線程隔離(倉壁模式)和熔斷降級,在服務故障時控制影響范圍,避免級聯失敗。
實際項目中,要根據業務場景選擇合適的規則 —— 比如秒殺接口用 warm up + 熱點參數限流,慢服務調用用線程池隔離,核心接口用熔斷降級。多測試、多調優,才能讓微服務更穩定~