SpringCloud 微服務組件:Sentinel 限流熔斷
前言
什麼是雪崩問題?
微服務之間相互調用,因爲調用鏈中的一個服務故障,引起整個鏈路都無法訪問的情況。
解決雪崩問題的常見方式有四種:
-
**超時處理:**設定超時時間,請求超過一定時間沒有響應就返回錯誤信息,不會無休止等待
-
**艙壁模式:**限定每個業務能使用的線程數,避免耗盡整個 tomcat 的資源,因此也叫線程隔離。
-
**熔斷降級:**由斷路器統計業務執行的異常比例,如果超出閾值則會熔斷該業務,攔截訪問該業務的一切請求。
-
**流量控制:**限制業務訪問的 QPS,避免服務因流量的突增而故障。
前三種是一種已經出現後的應對措施可以避免因服務故障引起的雪崩問題;後一種是一個預防方案,可以避免因瞬間高併發流量而導致服務故障
與 Hystrix 對比
簇點鏈路:就是項目內的調用鏈路,鏈路中被監控的每個接口就是一個資源。默認情況下 sentinel 會監控 SpringMVC 的每一個端點(Endpoint),因此 SpringMVC 的每一個端點(Endpoint)就是調用鏈路中的一個資源。
流控、熔斷等都是針對簇點鏈路中的資源來設置的,因此我們可以點擊對應資源後面的按鈕來設置規則:
Sentinel 概述
Sentinel (分佈式系統的流量防衛兵) 是阿里開源的一套用於服務容錯的綜合性解決方案。它以流量爲切入點, 從流量控制、熔斷降級、系統負載保護等多個維度來保護服務的穩定性。
Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景, 例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峯填谷、集羣流量控制、實時熔斷下游不可用應用等。
Sentinel 核心分爲兩個部分:
-
核心庫(Java 客戶端):能夠運行於所有 Java 運行時環境,同時對 Dubbo /Spring Cloud 等框架也有較好的支持。
-
控制檯(Dashboard):基於 Spring Boot 開發,打包後可以直接運行。
Sentinel 的使用分爲兩部分:
-
entinel-dashboard:與
hystrix-dashboard
類似,但是它更爲強大一些。除了與hystrix-dashboard
一樣提供實時監控之外,還提供了流控規則、熔斷規則的在線維護等功能。 -
客戶端整合:每個微服務客戶端都需要整合 sentinel 的客戶端封裝與配置,才能將監控信息上報給 dashboard 展示以及實時的更改限流或熔斷規則等。
安裝 Sentinel 服務
Sentinel 提供一個輕量級的控制檯, 它提供機器發現、單機資源實時監控以及規則管理等功能,其控制檯安裝步驟如下:
第一步:打開 sentinel 下載網址
https://github.com/alibaba/Sentinel/releases
第二步:下載 Jar 包(可以存儲到一個 sentinel 目錄)
第三步:在 sentinel 對應目錄,打開命令行 (cmd),啓動運行 sentinel
java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
檢測啓動過程,如圖所示:
訪問 Sentinal 服務
第一步:假如 Sentinal 啓動 ok,通過瀏覽器進行訪問測試,登陸 sentinel,默認用戶和密碼都是 sentinel,如圖所示:
sentinel-dashboard
不像 Nacos 的服務端那樣提供了外置的配置文件,比較容易修改參數。不過不要緊,由於sentinel-dashboard
是一個標準的 spring boot 應用,所以如果要自定義端口號等內容的話,可以通過在啓動命令中增加參數來調整,比如:-Dserver.port=8888
。
默認情況下,sentinel-dashboard
以 8080 端口啓。
對於用戶登錄的相關配置可以在啓動命令中增加下面的參數來進行配置:
-
-Dsentinel.dashboard.auth.username=sentinel
: 用於指定控制檯的登錄用戶名爲 sentinel; -
-Dsentinel.dashboard.auth.password=123456
: 用於指定控制檯的登錄密碼爲 123456; -
如果省略這兩個參數,默認用戶和密碼均爲 sentinel
-
-Dserver.servlet.session.timeout=7200
: 用於指定 Spring Boot 服務端 session 的過期時間,如 7200 表示 7200 秒;60m 表示 60 分鐘,默認爲 30 分鐘;
Sentinel 快速入門
第一步:Sentinel 應用於服務提供方 (provider),在服務提供方添加依賴如下:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
第二步:打開服務提供方配置文件xxx.yml
,添加 sentinel 配置,代碼如下:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8180 # 指定sentinel控制檯地址。
第三步:創建一個用於演示限流操作的 Controller 對象,例如:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/provider")
public class ProviderSentinelController {
@GetMapping("/sentinel01")
public String doSentinel01(){
return "sentinel 01 test ...";
}
}
啓動 sca-provider 服務,然後對指定服務進行訪問,如圖所示:
Sentinel 限流入門實踐
我們設置一下指定接口的流控 (流量控制),QPS(每秒請求次數)單機閾值爲 1,代表每秒請求不能超出 1 次,要不然就做限流處理,處理方式直接調用失敗。
第一步:選擇要限流的鏈路,如圖所示:
第二步:設置限流策略,如圖所示:
第三步:反覆刷新訪問消費端端服務,檢測是否有限流信息輸出,如圖所示:
Sentinel 流控規則分析
閾值類型
-
QPS(Queries Per Second) :當調用相關 url 對應的資源時,QPS 達到單機閾值時,就會限流。
-
線程數:當調用相關 url 對應的資源時,線程數達到單機閾值時,就會限流。
設置限流模式
Sentinel 的流控模式代表的流控的方式,默認【直接】,還有關聯,鏈路。
直接模式
Sentinel 默認的流控處理就是【直接 -> 快速失敗】。
關聯模式
當關聯的資源達到指定閾值,就限流自己。
例如設置了關聯資源爲 ur2 時,假如關聯資源 url2 的 QPS 閥值超過 1 時,就限流 url1 接口(是不是感覺很霸道,關聯資源達到閥值,是本資源接口被限流了)。
這種關聯模式有什麼應用場景呢?我們舉個例子,訂單服務中會有 2 個重要的接口,一個是讀取訂單信息接口,一個是寫入訂單信息接口。在高併發業務場景中,兩個接口都會佔用資源,如果讀取接口訪問過大,就會影響寫入接口的性能。
業務中如果我們希望寫入訂單比較重要,要優先考慮寫入訂單接口。那就可以利用關聯模式;在關聯資源上面設置寫入接口,資源名設置讀取接口就行了;這樣就起到了優先寫入,一旦寫入請求多,就限制讀的請求。例如
第一步:在ProviderSentinelController
中添加一個方法,例如:
@GetMapping("/sentinel02")
public String doSentinel02(){
return "sentinel 02 test ...";
}
第二步:在 sentinel 中做限流設計,
例如
第三步:打開兩個測試窗口,對/provider/sentinel02
進行訪問,檢查/provider/sentinel01
的狀態,例如:
鏈路模式
鏈路模式只記錄指定鏈路入口的流量。也就是當多個服務對指定資源調用時,假如流量超出了指定閾值,則進行限流。
被調用的方法用@SentinelResource
進行註解,然後分別用不同業務方法對此業務進行調用,假如 A 業務設置了鏈路模式的限流,在 B 業務中是不受影響的。現在對鏈路模式做一個實踐,例如:
第一步: 在指定 service 包創建一個ResourceService
類,代碼如下:
@Service
public class ResourceService{
@SentinelResource("doGetResource")
public String doGetResource(){
return "doGetResource";
}
}
第二步: 在ProviderSentinelController
中添加一個方法,例如:
@Autowired
private ResourceService resourceService;
@GetMapping("/sentinel03")
public String doSentinel03() throws InterruptedException {
resourceService.doGetResource();
return "sentinel 03 test";
}
第三步: 在 sentinel 中配置限流規則,例如:
設置鏈路流控規則後,再頻繁對限流鏈路進行訪問,檢測是否會出現 500 異常,例如
說明,流控模式爲鏈路模式時,假如是 sentinel 1.7.2 以後版本,Sentinel Web 過濾器默認會聚合所有 URL 的入口爲sentinel_spring_web_context
,因此單獨對指定鏈路限流會不生效,需要在 springboot 配置文件application.yml
中,添加如下語句來關閉URL PATH
聚合,例如:
sentinel:
web-context-unify: false
當設置了這個配置後,啓動服務,就可以對指定的特定鏈路進行限流了。
還有,當我們也可以基於@SentinelResource
註解描述的方法進行限流後的異常進行自定義處理,其步驟如下:
第一步: 定義blockHandlerClass
,例如:
package com.jt.provider.service;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ResourceBlockHandler {
/**
* 限流後的異常處理方法,應用於@SentinelResource註解中,
* 此方法在編寫時有如下幾個要求:
* 1)方法修飾符爲public
* 2)必須爲static方法
* 3)返回值類型與@SentinelResource註解描述的方法相同
* 4)參數類型爲BlockException
* 5)方法名自己定義
* @param ex
* @return
*/
public static String doHandle(BlockException ex){
log.error("block exception {}", ex.getMessage());
return "訪問太頻繁了,稍等片刻再訪問";
}
}
第二步: 修改@SentinelResource
註解中的屬性定義,例如:
@SentinelResource(value="doGetResource",
blockHandlerClass = ResourceBlockHandler.class,
blockHandler = "doHandle")
public String doGetResource(){
return "do get resource";
}
第三步: 在 controller 方法中,調用@Sentinel
註解描述的方法,例如:
/**
* 演示鏈路限流
* @return
*/
@GetMapping("/sentinel03")
public String doSentinel03(){
return resourceService.doGetResource();
//return "sentinel 03 test";
}
Sentinel 熔斷降級
除了流量控制以外,對調用鏈路中不穩定的資源進行熔斷降級也是保障高可用的重要措施之一。由於調用關係的複雜性,如果調用鏈路中的某個資源不穩定,最終會導致請求發生堆積。
Sentinel 熔斷降級會在調用鏈路中某個資源出現不穩定狀態時(例如調用超時或異常比例升高),對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導致級聯錯誤。當資源被降級後,在接下來的降級時間窗口之內,對該資源的調用都自動熔斷(默認行爲是拋出DegradeException
)。
準備工作
在ProviderController
類中添加doSentinel05
方法,基於此方法演示慢調用過程下的限流,代碼如下:
//AtomicLong 類支持線程安全的自增自減操作
private AtomicLong atomicLong=new AtomicLong(1);
@GetMapping("/sentinel05")
public String doSentinel05() throws InterruptedException {
//獲取自增對象的值,然後再加1
long num=atomicLong.getAndIncrement();
if(num%2==0){//模擬50%的慢調用比例
Thread.sleep(200);
}
return "sentinel 04 test";
}
說明, 我們在此方法中設置休眠, 目的是爲了演示慢調用 (響應時間比較長).
Sentinel 降級入門
接下來, 我們基於一個請求鏈路, 進行服務降級及應用實踐, 例如:
第一步:服務啓動後,選擇要降級的鏈路,如圖所示:
第二步:選擇要降級的鏈路,如圖所示:
這裏表示熔斷策略選擇 "慢調用比例",表示請求數超過 3 時,假如平均響應時間超過 200 毫秒的有 30%,則對請求進行熔斷,熔斷時長爲 20 秒鐘,10 秒以後恢復正常。
第三步:對指定鏈路進行刷新,多次訪問測試,檢測頁面上是否會出現限流(默認異常爲DegradeException
)
我們也可以進行斷點調試,在DefaultBlockExceptionHandler
中的 handle 方法內部加斷點,分析異常類型,假如異常類型DegradeException
則爲降級熔斷。
Sentinel 異常處理
系統提供了默認的異常處理機制,假如默認處理機制不滿足我們需求,我們可以自己進行定義。定義方式上可以直接或間接實現BlockExceptionHandler
接口,並將對象交給 spring 管理。
package com.jt.provider.controller;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* 自定義限流,降級等異常處理對象
*/
@Slf4j
@Component
public class ServiceBlockExceptionHandler
implements BlockExceptionHandler {
/**
* 用於處理BlockException類型以及子類類型異常
*/
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
BlockException e) throws Exception {
//設置響應數據編碼
response.setCharacterEncoding("utf-8");
//告訴客戶端響應數據的類型,以及客戶端顯示內容的編碼
response.setContentType("text/html;charset=utf-8");
//向客戶端響應一個json格式的字符串
//String str="{\"status\":429,\"message\":\"訪問太頻繁了\"}";
Map<String,Object> map=new HashMap<>();
map.put("status", 429);
map.put("message","訪問太頻繁了");
String jsonStr=new ObjectMapper().writeValueAsString(map);
PrintWriter out = response.getWriter();
out.print(jsonStr);
out.flush();
out.close();
}
}
Sentinel 熱點規則分析
-
何爲熱點?熱點即經常訪問的數據。比如:
-
商品 ID 爲參數,統計一段時間內最常購買的商品 ID 並進行限制。
-
用戶 ID 爲參數,針對一段時間內頻繁訪問的用戶 ID 進行限制。
-
熱點參數限流會統計傳入參數中的熱點數據,並根據配置的限流閾值與模式,對包含熱點參數的資源調用進行限流。熱點參數限流可以看做是一種特殊的流量控制,僅對包含熱點參數的資源調用生效。其中,Sentinel 會利用 LRU 策略統計最近最常訪問的熱點參數,結合令牌桶算法來進行參數級別的流控。
-
注意的是,熱點參數限流對默認的 SpringMVC 資源無效,只有使用
SentinelResource
標註的資源有用。
第一步:在sca-provider
中添加如下方法, 例如:
@GetMapping("/sentinel/findById")
@SentinelResource("resource")
public String doFindById(@RequestParam("id") Integer id){
return "resource id is "+id;
}
第二步:服務啓動後,選擇要限流的熱點鏈路,如圖所示:
第三步:設置要限流的熱點,如圖所示:
熱點規則的限流模式只有 QPS 模式(這才叫熱點)。參數索引爲@SentinelResource
註解的方法參數下標,0 代表第一個參數,1 代表第二個參數。單機閾值以及統計窗口時長表示在此窗口時間超過閾值就限流。
第四步:多次訪問熱點參數方法,前端會出現如下界面,如圖所示:
然後,在後臺出現如下異常表示限流成功。
com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException: 2
其中,熱點參數其實說白了就是特殊的流控,流控設置是針對整個請求的;但是熱點參數他可以設置到具體哪個參數,甚至參數針對的值,這樣更靈活的進行流控管理。
一般應用在某些特殊資源的特殊處理,如:某些商品流量大,其他商品流量很正常,就可以利用熱點參數限流的方案。
特定參數【熱點限流中的某個參數值的閾值設計】設計
配置參數例外項,如圖所示:
其中, 這裏表示參數值爲 5 時閾值爲 100,其它參數值閾值爲 1.
Sentinel 系統規則
-
系統在生產環境運行過程中,我們經常需要監控服務器的狀態,看服務器 CPU、內存、IO 等的使用率;主要目的就是保證服務器正常的運行,不能被某些應用搞崩潰了;而且在保證穩定的前提下,保持系統的最大吞吐量。
-
sentinel 中的系統規則,是對所有鏈路的控制規則, 是一種系統保護策略,Sentinel 系統保護規則被觸發以後底層會拋出
SystemBlockException
異常。 -
Sentinel 的系統保護規則是從應用級別的入口流量進行控制,從單臺機器的總體 Load(負載)、RT(響應時間)、入口 QPS 、線程數和 CPU 使用率五個維度監控應用數據,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。如圖所示:
系統規則是一種全局設計規則,其中,
-
Load(僅對 Linux/Unix-like 機器生效):當系統 load1 超過閾值,且系統當前的併發線程數超過系統容量時纔會觸發系統保護。系統容量由系統的
maxQps * minRt
計算得出。設定參考值一般是CPU cores * 2.5
。 -
CPU 使用率:當系統 CPU 使用率超過閾值即觸發系統保護(取值範圍 0.0-1.0)。
-
RT:當單臺機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。
-
線程數:當單臺機器上所有入口流量的併發線程數達到閾值即觸發系統保護。
-
入口 QPS:當單臺機器上所有入口流量的 QPS 達到閾值即觸發系統保護。
說明,系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量生效。入口流量指的是進入應用的流量(EntryType.IN
),比如 Web 服務。
Sentinel 授權規則
很多時候,我們需要根據調用方來限制資源是否通過,這時候可以使用 Sentinel 的黑白名單控制的功能。**黑白名單根據資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位於白名單內時纔可通過;若配置黑名單則請求來源位於黑名單時不通過,其餘的請求通過。**例如微信中的黑名單。
快速入門
sentinel 可以基於黑白名單方式進行授權規則設計,如圖所示:
黑白名單規則(AuthorityRule)非常簡單,主要有以下配置項:
-
資源名: 即限流規則的作用對象
-
流控應用: 對應的黑名單 / 白名單中設置的規則值, 多個值用逗號隔開.
-
授權類型: 白名單,黑名單 (不允許訪問).
案例實現:
定義請求解析器, 用於對請求進行解析, 並返回解析結果, sentinel 底層在攔截到用戶請求以後, 會對請求數據基於此對象進行解析, 判定是否符合黑白名單規則, 例如:
第一步: 定義RequestOriginParser
接口的實現類,在接口方法中解析請求參數數據並返回, 底層會基於此返回值進行授權規則應用。
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String origin = request.getParameter("origin");//這裏的參數名會與請求中的參數名一致
return origin;
}
}
第二步: 定義流控規則, 如圖所示:
第三步: 執行資源訪問, 檢測授權規則應用, 當我們配置的流控應用值爲 app1 時,假如規則爲黑名單,則基於http://ip:port/path?origin=app1
的請求不可以通過, 其請求處理流程如圖下:
拓展:嘗試基於請求 ip 等方式進行黑白名單的規則設計, 例如:
第一步: 修改請求解析器, 獲取請求 ip 並返回, 例如:
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
//解析請求源數據
@Override
public String parseOrigin(HttpServletRequest request) {
//獲取訪問請求中的ip地址,基於ip地址進行黑白名單設計(例如在流控應用欄寫ip地址)
String ip= request.getRemoteAddr();
System.out.println("ip="+ip);
return ip;
}//授權規則中的黑白名單的值,來自此方法的返回值
}
第二步: 在 sentinel 控制檯定義授權規則, 例如:
第三步: 規則定義後以後, 基於你的 ip 地址, 進行訪問測試, 檢測黑白名單效果.
-
Sentinel 中的授權規則: 對指定資源的訪問給出的一種簡易的授權策略
-
Sentinel 如何識別白名單和黑名單?(在攔截器中通過調用
RequestOriginParser
對象的方法檢測具體的規則) -
授權規則中 RequestOriginParser 類的做用是什麼?(對流控應用值進行解析,檢查服務訪問時傳入的值是否與
RequestOriginParser
的parseOrigin
方法返回值是否相同。)
來源:blog.csdn.net/qq_25447799/article/
details/123122820
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/MXIsrdYLN7XvYZaCY-imfw