SpringCloud Alibaba微服務實戰五 – 限流熔斷
- 2019 年 12 月 17 日
- 筆記
簡介
Sentinel
是面向分散式服務框架的輕量級流量控制框架,主要以流量為切入點,從流量控制,熔斷降級,系統負載保護等多個維度來維護系統的穩定性。在SpringCloud體系中,sentinel
主要是為了替換原Hystrix的功能,與Hystrix相比,sentinel的隔離級別更加精細,提供的Dashboard可以在線更改限流熔斷規則,而且使用也越加方便。要了解更多詳細資訊請移步至Sentinel官網。
基礎準備
要使用Sentinel提供的限流熔斷能力,需要先做如下準備:
- 安裝Sentinel 這部分內容我已經在第一期SpringCloud Alibaba微服務實戰一 – 基礎環境準備中提過,大家可以翻閱查看。
- 引入Sentinel 在需要配置限流熔斷服務的POM文件中引入Sentinel組件
<!--Sentinel--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
- 自定義資源
@SentinelResource
我們只需要在相關方法上加上@SentinelResource
註解,讓其可以成為sentinel識別的資源即可。如:
@GetMapping("/account/getByCode/{accountCode}") @SentinelResource(value = "getByCode") public ResultData<AccountDTO> getByCode(@PathVariable(value = "accountCode") String accountCode){ log.info("get account detail,accountCode is :{}",accountCode); AccountDTO accountDTO = accountService.selectByCode(accountCode); return ResultData.success(accountDTO); }
- 在配置文件中添加sentinel的服務端地址
server: port: 8010 spring: application: name: account-service cloud: nacos: discovery: server-addr: 192.168.0.107:8848/ sentinel: transport: # sentinel服務端地址 dashboard: 192.168.0.107:8858 # 取消延遲載入 eager: true
經過以上幾步我們準備好了使用Sentinel的基礎環境,接下來我們看看限流熔斷的具體配置。
限流
概念說明
生產者accout-service
是一個核心服務,我們通過壓測得出服務的最大負載能力為60。如果某個時間account-service
的請求數飆升達到了600,那服務肯定就直接gg了。所以為了保護我們的accout-service
,我們會給它配置一個限流規則,如果每秒鐘有超過60的請求那不好意思我直接丟掉不處理了,然後丟給消費者一個異常,想拖垮我,哼,沒門!。

總而言之,限流是通過限制調用方對自己的調用,起到保護自己系統的效果。
限流配置
理想是豐滿的,現實是骨感的。由於本人對Jmeter之類的壓測工具不是很精通所以為了方便測試,我們就將accout-service
的QPS單機閾值設置成5,如果每秒QPS超過5,直接丟棄。

這裡的資源名就是我們使用@SentinelResource
註解自定義的資源。
打開瀏覽器,快速刷新瀏覽器,當每秒請求數超過5時會看到如下錯誤:

在後端服務日誌中你會看到如下的錯誤日誌:
2019-12-10 14:22:31,948 ERROR [dispatcherServlet]:175 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.reflect.UndeclaredThrowableException] with root cause com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
不要慌,這說明我們的目的達到了,限流成功!
自定義異常
我們可以通過@SentinelResource
中添加blockHandler
參數,給其添加自定義異常方法。如:
@GetMapping("/account/getByCode/{accountCode}") @SentinelResource(value = "getByCode",blockHandler = "handleException") public ResultData<AccountDTO> getByCode(@PathVariable(value = "accountCode") String accountCode){ log.info("get account detail,accountCode is :{}",accountCode); AccountDTO accountDTO = accountService.selectByCode(accountCode); return ResultData.success(accountDTO); } /** * 自定義異常策略 * 返回值和參數要跟目標函數一樣,參數可以追加BlockException */ public ResultData<AccountDTO> handleException(String accountCode,BlockException exception){ log.info("flow exception{}",exception.getClass().getCanonicalName()); return ResultData.fail(900,"達到閾值了,不要再訪問了!"); }
注意,自定義的異常方法的參數和返回值要跟目標方法一樣,參數可以追加BlockException
效果如下:

比之前的那個錯誤頁優雅多了有木有!
持久化配置
由於Sentinel
的配置默認是放在記憶體中的,每當應用重啟或者sentinel
重啟都會丟失數據,我們這裡使用Nacos作為配置中心持久化限流配置。
- 修改pom文件,引入
sentinel-datasource-nacos
組件
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
- 修改application.yml,配置sentinel的數據源
spring: cloud: sentinel: datasource: ds: nacos: server-addr: 10.0.10.48:8848 data-id: ${spring.application.name}-sentinel group-id: DEFAULT_GROUP rule-type: flow
- 在nacos中建立限流配置
account-service-sentinel
(配置格式設置成json)
[ { "resource": "getByCode", "limitApp": "default", "grade": 1, "count": 3, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ]
可以看到上面配置規則是一個數組類型,數組中的每個對象是針對每一個保護資源的配置對象,每個對象中的屬性解釋如下:
resource
:資源名,即限流規則的作用對象limitApp
:流控針對的調用來源,若為 default 則不區分調用來源grade
:限流閾值類型(QPS 或並發執行緒數);0代表根據並發數量來限流,1代表根據QPS來進行流量控制count
:限流閾值strategy
:調用關係限流策略controlBehavior
:流量控制效果(直接拒絕、Warm Up、勻速排隊)clusterMode
:是否為集群模式
- 進入
sentinel
查看dashboard,發現sentinel自動獲取nacos的配置

- 頻繁刷新瀏覽器調用介面,驗證介面是否正常限流
熔斷
概念說明
消費者order-service
需要先調用product-service
獲取具體的product,然後再處理其他的業務邏輯。但是這個product-service
介面不是很穩定,經常拋出異常;或者是響應緩慢,導致order-service
的響應變慢;如果置之不理,order-service
可能會被product-service
拖垮。這時候為了保護order-service
,我們需要對product-service
介面進行熔斷。

image.png
一言以蔽之:熔斷是通過限制自己對外部系統的調用, 起到節約響應時間、維護鏈路穩定的作用。
熔斷配置
Sentinel中的熔斷降級有三個降級策略:
- RT(平均響應時間):當資源的平均響應時間超過閾值之後,資源進入准降級狀態。接下來如果持續進入 5 個請求,它們的 RT 都持續超過這個閾值,那麼在接下的時間窗口之內,對這個方法的調用都會自動拋出 DegradeException 異常。在下一個時間窗口到來時, 會接著再放入5個請求, 再重複上面的判斷.
- 異常比例 當資源的每秒異常總數占通過量的比值超過閾值之後,資源進入降級狀態,即在接下的時間窗口之內,對這個方法的調用都會自動地拋出DegradeException異常。異常比率的閾值範圍是 [0.0, 1.0],代表 0% – 100%。
- 異常數 當資源近 1 分鐘的異常數目超過閾值之後會進行熔斷。
首先我們對原介面進行改造,讓其直接拋出Runtimeexception
:
@GetMapping("/product/getByCode/{productCode}") @SentinelResource(value = "/product/getByCode",fallback = "fallbackHandler") public ResultData<ProductDTO> getByCode(@PathVariable String productCode){ log.info("get product detail,productCode is :{}",productCode); ProductDTO productDTO = productService.selectByCode(productCode); throw new RuntimeException("error"); // return ResultData.success(productDTO); }
這裡我們將product-service
設置如下的熔斷規則:

如果/product/getByCode
的異常率超過50%,那麼接下來2秒內直接觸發熔斷降級,默認情況會拋出DegradeException
異常,如:
2019-12-10 19:35:53,764 ERROR [dispatcherServlet]:175 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.reflect.UndeclaredThrowableException] with root cause com.alibaba.csp.sentinel.slots.block.degrade.DegradeException: null
自定義異常
自定義熔斷異常跟限流異常類似,我們使用fallback屬性指定自定義異常的方法,如:
@SentinelResource(value = "/product/getByCode",fallback = "fallbackHandler") public ResultData<ProductDTO> getByCode(@PathVariable String productCode){ ... } /** * 自定義熔斷異常 * 返回值和參數要跟目標函數一樣 */ public ResultData<ProductDTO> fallbackHandler(String productCode){ return ResultData.fail(800,"服務被熔斷了,不要調用!"); }
注意,自定義的異常方法的參數和返回值要跟目標方法一樣
效果如下:

持久化配置
- 引入
sentinel-datasource-nacos
組件,跟限流一樣配置即可 - 修改application.yml,配置sentinel的數據源
spring: cloud: sentinel: datasource: ds: nacos: server-addr: 192.168.0.106:8848 data-id: ${spring.application.name}-sentinel-degrade group-id: DEFAULT_GROUP rule-type: degrade
- 在nacos中建立配置文件
product-service-sentinel-degrade
,做如下配置
[ { "resource": "/product/getByCode", "count": 0.5, "grade": 1, "passCount": 0, "timeWindow": 2 } ]
可以看到上面配置規則是一個數組類型,數組中的每個對象是針對每一個保護資源的配置對象,每個對象中的屬性解釋如下:
resource
:資源名,即降級規則的作用對象count
:閾值grade
:降級模式 0:RT 1:異常比例 2:異常數timeWindow
:時間窗口(單位秒)
- 進入sentinel查看dashboard,發現sentinel自動獲取nacos的配置

血與淚
大家在使用sentinel過程中如果出現Failed to fetch metric from
的錯誤,具體表現如下:
Failed to fetch metric from <http://192.168.136.1:8719/metric?startTime=1563865044000&endTime=1563865050000&refetch=false> (ConnectionException: Connection refused: no further information)
這個時候你需要去檢查下sentinel控制台的服務列表,確認是否跟你ip一致。(我之前是裝過虛擬機,sentinel一直抓取的是我虛擬的ip,不知道為什麼。。。)

如果發現監聽的地址不對的話,可以在sentinel客戶端配置中加入客戶端ip配置
spring: cloud: sentinel: transport: client-ip: 192.168.0.108
至此我們已經給我們的微服務加上了限流熔斷保護,再也不用擔心異常流量的衝擊,下游系統不穩定導致自身服務不可用了。那麼本期的「SpringCloud Alibaba微服務實戰五 – 限流熔斷」篇也就該結束啦,咱們下期有緣再見!

系列文章
- SpringCloud Alibaba微服務實戰一 – 基礎環境準備
- SpringCloud Alibaba微服務實戰二 – 服務註冊
- SpringCloud Alibaba微服務實戰三 – 服務調用
- SpringCloud Alibaba微服務實戰四 – 版本管理