一文說透Sentinel熔斷策略、降級規則、流量控制

来源:https://www.cnblogs.com/jiagooushi/archive/2022/06/08/16355571.html
-Advertisement-
Play Games

2 Sentinel 限流熔斷降級 Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。我們先來學習Sentinel 核心庫的使用,後面再學習Dashboard使用。 在我們項目中,用戶 ...


2 Sentinel 限流熔斷降級

Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。我們先來學習Sentinel 核心庫的使用,後面再學習Dashboard使用。

file

在我們項目中,用戶請求通過hailtaxi-gateway路由到hailtaxi-driver或者hailtaxi-order,還有可能在hailtaxi-order中使用feign調用hailtaxi-driver,所以我們有可能在單個服務中實現熔斷限流,也有可能要集成feign調用實現熔斷限流,還有可能在微服務網關中實現熔斷限流。我們接下來一步一步實現每一種熔斷限流操作。

SpringBoot集成:

如果在SpringBoot項目中使用Sentinel,首先需要引入spring-cloud-starter-alibaba-sentinel依賴,並使用@SentinelResource標識資源。

hailtaxi-driver工程中引入spring-cloud-starter-alibaba-sentinel依賴,依賴如下:

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

2.1 @SentinelResource定義資源

@SentinelResource 用於定義資源,並提供可選的異常處理和 fallback 配置項。 @SentinelResource 註解包含以下屬性:

value 資源名稱,必需項(不能為空)
blockHandler / blockHandlerClass blockHandler 對應處理 BlockException 的函數名稱,可選項。
♞ blockHandler 函數訪問範圍需要是 public;
♞ 返回類型需要與原方法相匹配,參數類型需要和原方法相匹配並且最後加一個額外的參數,類型為 BlockException。
♞ blockHandler 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 blockHandlerClass 為對應的類的 Class 對象,註意對應的函數必需為 static 函數,否則無法解析。
fallback / fallbackClass fallback 函數名稱,可選項,用於在拋出異常的時候提供 fallback 處理邏輯。fallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore 裡面排除掉的異常類型)進行處理。fallback 函數簽名和位置要求:
♞ 返回值類型必須與原函數返回值類型一致;
♞ 方法參數列表需要和原函數一致,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。
♞ fallback 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,註意對應的函數必需為 static 函數,否則無法解析。
defaultFallback(1.6.0 開始) 預設的 fallback 函數名稱,可選項,通常用於通用的 fallback 邏輯(即可以用於很多服務或方法)。預設 fallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore 裡面排除掉的異常類型)進行處理。若同時配置了 fallback 和 defaultFallback,則只有 fallback 會生效。defaultFallback 函數簽名要求:
♞ 返回值類型必須與原函數返回值類型一致;
♞ 方法參數列表需要為空,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。
♞ defaultFallback 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,註意對應的函數必需為 static 函數,否則無法解析。
exceptionsToIgnore(1.6.0 開始) 用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣拋出。
entryType entry 類型,可選項(預設為 EntryType.OUT)

blockHandler/blockHandlerClass

hailtaxi-driver中找到DriverController中的info方法,用戶在打車的時候,會查詢司機信息,如果司機不存在,此時會報錯,代碼改造如下:

/****
     * 司機信息
     */
@GetMapping(value = "/info/{id}")
//@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id,HttpServletRequest request){
    log.info("當前服務占用的埠為:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        throw new RuntimeException("司機id="+id+"不存在");
    }
    return driver;
}

如果此時訪問:http://localhost:18081/driver/info/3 查詢司機信息,如果沒有ID為3的司機信息,會報如下錯誤,

file

這種體驗非常差,我們可以集成Sentinel使用@SentinelResourceblockHandler返回預設錯誤信息,形成降級!!!

1、Sentinel 支持在程式中拋出它定義的BlockException異常,該異常會被Sentinel捕獲,然後走降級方法,

info()方法添加一個@SentinelResource註解,用來標註資源,表示當前方法需要執行限流、降級,在註解中添加value屬性,用來標註資源,說白了就是給當前資源起個名字,blockHandler用來表示當前方法發生BlockException異常的時候,將處理流程交給指定的方法blockExHandler()處理,此時blockExHandler()方法必須和拋出異常的方法在同一個類中,這是一種降級操作,代碼如下:

/****
     * 司機信息
     */
@SentinelResource(value = "info",blockHandler = "blockExHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
    log.info("當前服務占用的埠為:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        //throw new RuntimeException("司機id="+id+"不存在");
        throw new SystemBlockException("info", "司機id="+id+"不存在",null); // 拋出BlockException
    }
    return driver;
}

/**
     * info資源出現BlockException後的降級處理
     */
public Driver blockExHandler(String id,BlockException e) {
    Driver driver = new Driver();
    driver.setId(id);
    driver.setName("系統繁忙,稍後再試");
    return driver;
}

file

註意:

如果blockHandler方法和資源方法不在同一個類中,我們可以在@SentinelResource中添加blockHandlerClass屬性,指定降級處理類的方法所在的類,且要求blockHandler方法是靜態的,代碼如下:

@SentinelResource(value = "info",blockHandler = "blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx")

2、啟動測試,訪問:http://localhost:18081/driver/info/3 測試出錯效果如下:

file

fallback/fallbackClass

1、如果我們希望拋出任何異常都能處理,都能調用預設處理方法,而並非只是BlockException異常才調用,此時可以使用@SentinelResourcefallback屬性,代碼如下:

/****
     * 司機信息
     */
@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
    log.info("當前服務占用的埠為:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        throw new RuntimeException("司機id="+id+"不存在");
        // throw new SystemBlockException("info", "司機id="+id+"不存在",null); // 拋出BlockException
    }
    return driver;
}
/**
     * info資源出現任何類型異常後的降級處理
     * 方法參數可以添加一個Throwable 類型的參數,也可不添加
     */
public Driver exHandler(String id,Throwable e) {
    Driver driver = new Driver();
    driver.setId(id);
    driver.setName("系統繁忙,稍後再試");
    return driver;
}

註意:

如果fallback方法和當前的資源方法不在同一個類中,可以使用@SentinelResource註解的fallbackClass實現,也要求fallback方法是靜態的,代碼如下:

@SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass = "xx.xxx.xxx.xx.Xxx")

2、訪問 http://localhost:18081/driver/info/3 測試出錯效果如下:

file

defaultFallback

上面無論是blockHandler還是fallback,每個方法發生異常,都要為方法獨立創建一個處理異常的方法,效率非常低,我們可以使用@SentinelResource註解的defaultFallback屬性,為一個類指定一個全局的處理錯誤的方法,代碼如下:

@RestController
@RequestMapping(value = "/driver")
@Slf4j
@RefreshScope
@SentinelResource(defaultFallback = "defaultExHandler")
public class DriverController {
    @Autowired
    private DriverService driverService;

    public Driver defaultExHandler(Throwable e) {
        Driver driver = new Driver();
        driver.setName("系統繁忙,稍後再試");
        return driver;
    }

    /****
     * 司機信息
     */
    //@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
    @SentinelResource("info")
    @RequestMapping(value = "/info/{id}")
    public Driver info(@PathVariable(value = "id")String id) throws BlockException {
        log.info("當前服務占用的埠為:{}",port);
        Driver driver = driverService.findById(id);
        if (driver==null) {
            throw new RuntimeException("司機id="+id+"不存在");
            // throw new SystemBlockException("info", "司機id="+id+"不存在",null); // 拋出BlockException
        }
        return driver;
    }

訪問 http://localhost:18081/driver/info/3 效果如下:

file

2.2 Sentinel的規則

Sentinel 的所有規則都可以在記憶體態中動態地查詢及修改,修改之後立即生效。同時 Sentinel 也提供相關 API,供您來定製自己的規則策略。

Sentinel 支持以下幾種規則:流量控制規則熔斷降級規則系統保護規則來源訪問控制規則熱點參數規則

2.2.1 流量控制規則 (FlowRule)

流量規則的定義,重要屬性如下:

Field 說明 預設值
resource 資源名,資源名是限流規則的作用對象
count 限流閾值
grade 限流閾值類型,QPS 模式(1)或併發線程數模式(0) QPS 模式
limitApp 流控針對的調用來源 default,代表不區分調用來源
strategy 調用關係限流策略:直接、鏈路、關聯 根據資源本身(直接)
controlBehavior 流控效果(直接拒絕/WarmUp/勻速+排隊等待),不支持按調用關係限流 直接拒絕
clusterMode 是否集群限流

同一個資源可以同時有多個限流規則,檢查規則時會依次檢查

strategy限流策略說明:

直接:資源達到限流條件時,直接限流。

關聯:A資源關聯B資源,當關聯的B資源達到閾值限流時,A資源也會被限流。

鏈路:對於某資源C,有兩個入口,從資源A->C,從資源B->C, 通過指定入口資源可以達到只記錄從該入口進來的流量(指定資源從入口資源進來的流量,如果達到閾值,就可以對其限流)。

controlBehavior流控說明:

直接拒絕:請求直接失敗。
WarmUp:當系統長期處於低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。
排隊等待:排隊處理請求。

理解上面規則的定義之後,我們可以通過調用 FlowRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

QPS流量控制

1、我們先實現基於QPS流量控制,在hailtaxi-driverDriverApplication啟動類上添加如下方法載入限流規則,當DriverApplication初始化完成之後載入規則,代碼如下:

/***
 * 初始化規則
 */
@PostConstruct
private void initFlowRule() {
    //規則集合
    List<FlowRule> rules = new ArrayList<FlowRule>();
    //定義一個規則
    FlowRule rule = new FlowRule("info");
    // 設置閾值
    rule.setCount(2);
    //設置限流閾值類型
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    //default,代表不區分調用來源
    rule.setLimitApp("default");
    //設置流控效果
    rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
    //將定義的規則添加到集合中
    rules.add(rule);
    //載入規則
    FlowRuleManager.loadRules(rules);
}

2、訪問 http://localhost:18081/driver/info/1 此時不會拋出異常,但是頻繁刷新,則會調用降級方法,效果如下:

file

線程數流量控制

1、我們修改限流閾值類型,代碼如下:

@PostConstruct
public void initFlowRule() {
    //規則集合
    List<FlowRule> rules = new ArrayList<>();
    // 定義一個規則
    FlowRule rule = new FlowRule("info");
    // 設置基流量控制 的類型
    rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);//預設是qps
    //設置流量閾值
    rule.setCount(2);
    // 將 規則添加到 集合中
    rules.add(rule);
    // 載入規則
    FlowRuleManager.loadRules(rules);
}

2、此時再來訪問http://localhost:18081/driver/info/1我們發現用瀏覽器無論怎麼訪問都不會出現降級現象,但是如果用Jmeter模擬多個線程,效果就不一樣了,效果如下:

file

2.2.2 熔斷降級規則 (DegradeRule)

熔斷降級規則包含下麵幾個重要的屬性:

Field 說明 預設值
resource 資源名,即規則的作用對象
grade 熔斷策略,支持慢調用比例/異常比例/異常數策略 慢調用比例
count 慢調用比例模式下為慢調用臨界 RT(超出該值計為慢調用);異常比例/異常數模式下為對應的閾值
timeWindow 熔斷時長,單位為 s
minRequestAmount 熔斷觸發的最小請求數,請求數小於該值時即使異常比率超出閾值也不會熔斷(1.7.0 引入) 5
statIntervalMs 統計時長(單位為 ms),如 60*1000 代表分鐘級(1.8.0 引入) 1000 ms
slowRatioThreshold 慢調用比例閾值,僅慢調用比例模式有效(1.8.0 引入)

同一個資源可以同時有多個降級規則。

理解上面規則的定義之後,我們可以通過調用 DegradeRuleManager.loadRules() 方法來用硬編碼的方式定義熔斷規則,

1、在DriverApplication規則定義如下:

@PostConstruct
public void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    // 設置資源名稱
    rule.setResource("info");
    /**
         * 設置熔斷策略
         * DEGRADE_GRADE_RT:平均響應時間
         * DEGRADE_GRADE_EXCEPTION_RATIO:異常比例數量
         * DEGRADE_GRADE_EXCEPTION_COUNT:異常數
         */
    rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
    //設置閾值
    rule.setCount(2);
    //設置 熔斷時長
    rule.setTimeWindow(30);
    // 統計時長(單位為 ms) 預設1000
    rule.setStatIntervalMs(60*1000);
    //將規則添加到集合中
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

2、首先訪問:http://localhost:18081/driver/info/1 ,確保沒問題,

其次訪問:http://localhost:18081/driver/info/3,多訪問幾次,造成熔斷(5+2=7)

然後在訪問:http://localhost:18081/driver/info/1,會發現已經有熔斷降級效果了,

且查看服務控制台,發現不會有信息輸出,表明已經熔斷了,且從頁面展示效果來看走了降級

file

等待30s後,再次訪問:http://localhost:18081/driver/info/1,查看熔斷是否結束!

2.2.3 系統保護規則 (SystemRule)

Sentinel 系統自適應限流從整體維度對應用入口流量進行控制,結合應用的 Load、CPU 使用率、總體平均 RT、入口 QPS 和併發線程數等幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。

系統規則包含下麵幾個重要的屬性:

Field 說明 預設值
highestSystemLoad load1 觸發值,用於觸發自適應控制階段 -1 (不生效)
avgRt 所有入口流量的平均響應時間 -1 (不生效)
maxThread 入口流量的最大併發數 -1 (不生效)
qps 所有入口資源的 QPS -1 (不生效)
highestCpuUsage 當前系統的 CPU 使用率(0.0-1.0) -1 (不生效)

理解上面規則的定義之後,我們可以通過調用 SystemRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

1、在hailtaxi-driverDriverApplication中創建如下方法,代碼如下:

/***
 * 系統自我保護
 */
@PostConstruct
private void initSystemRule() {
    //系統自我保護集合
    List<SystemRule> rules = new ArrayList<>();
    //創建系統自我保護規則
    SystemRule rule = new SystemRule();
    //CPU使用率 值為[0,1],-1 (不生效)
    rule.setHighestCpuUsage(0.2);
    //所有入口資源的 QPS,-1 (不生效)
    rule.setQps(10);
    //入口流量的最大併發數,-1 (不生效)
    rule.setMaxThread(5);
    //所有入口流量的平均響應時間,單位:秒,-1 (不生效)
    rule.setAvgRt(5);
    //load1 觸發值,用於觸發自適應控制階段,系統最高負載,建議取值 CPU cores * 2.5
    rule.setHighestSystemLoad(20);
    //將規則加入到集合
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}

我們可以測試CPU使用率自我保護,使用jmeter測試如下:

file

2.2.4 訪問控制規則 (AuthorityRule)

很多時候,我們需要根據調用方來限制資源是否通過,這時候可以使用 Sentinel 的訪問控制(黑白名單)的功能。黑白名單根據資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位於白名單內時才可通過;若配置黑名單則請求來源位於黑名單時不通過,其餘的請求通過。

授權規則,即黑白名單規則(AuthorityRule)非常簡單,主要有以下配置項:

  • resource:資源名,即規則的作用對象
  • limitApp:對應的黑名單/白名單,不同 origin 用 , 分隔,如 appA,appB
  • strategy:限制模式,AUTHORITY_WHITE 為白名單模式,AUTHORITY_BLACK 為黑名單模式,預設為白名單模式

瞭解了以上規則後,可以通過AuthorityRuleManager.loadRules來載入規則

1、在hailtaxi-driverDriverApplication中創建如下方法,代碼如下:

@PostConstruct
    public void initAuthorityRule() {
        AuthorityRule rule = new AuthorityRule();
        rule.setResource("info");
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
        rule.setLimitApp("127.0.0.1,appB");
        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
    }

    /**
     * Sentinel提供了 RequestOriginParser 介面來處理訪問來源,Sentinel保護的資源如果被訪問,
     * 就會調用 RequestOriginParser解析訪問來源
     */
    @Component
    public class IpLimiter implements RequestOriginParser{

        @Override
        public String parseOrigin(HttpServletRequest httpServletRequest) {
            return httpServletRequest.getRemoteAddr();
        }
    }

2、訪問:http://localhost:18081/driver/info/1,不通過,走了降級

​ 訪問:http://127.0.0.1:18081/driver/info/1

2.2.4 熱點規則 (ParamFlowRule)

何為熱點?熱點即經常訪問的數據。

很多時候我們希望統計某個熱點數據中訪問頻次最高的 Top K 數據,並對其訪問進行限制。比如:

1:商品 ID 為參數,統計一段時間內最常購買的商品 ID 併進行限制
2:用戶 ID 為參數,針對一段時間內頻繁訪問的用戶 ID 進行限制

熱點參數限流會統計傳入參數中的熱點參數,並根據配置的限流閾值與模式,對包含熱點參數的資源調用進行限流。熱點參數限流可以看做是一種特殊的流量控制,僅對包含熱點參數的資源調用生效。

file

Sentinel 利用 LRU 策略統計最近最常訪問的熱點參數,結合令牌桶演算法來進行參數級別的流控。熱點參數限流支持集群模式。

要使用熱點參數限流功能,需要引入以下依賴,將該依賴加入到hailtaxi-driver中:

<!--熱點參數限流-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>1.8.1</version>
</dependency>

然後為對應的資源配置熱點參數限流規則,併在 entry 的時候傳入相應的參數,即可使熱點參數限流生效。

熱點參數規則(ParamFlowRule)類似於流量控制規則(FlowRule):

屬性 說明 預設值
resource 資源名,必填
count 限流閾值,必填
grade 限流模式 QPS 模式
durationInSec 統計視窗時間長度(單位為秒),1.6.0 版本開始支持 1s
controlBehavior 流控效果(支持快速失敗和勻速排隊模式),1.6.0 版本開始支持 快速失敗
maxQueueingTimeMs 最大排隊等待時長(僅在勻速排隊模式生效),1.6.0 版本開始支持 0ms
paramIdx 熱點參數的索引,必填,對應 SphU.entry(xxx, args) 中的參數索引位置
paramFlowItemList 參數例外項,可以針對指定的參數值單獨設置限流閾值,不受前面 count 閾值的限制。僅支持基本類型和字元串類型
clusterMode 是否是集群參數流控規則 false
clusterConfig 集群流控相關配置

我們可以通過 ParamFlowRuleManagerloadRules 方法更新熱點參數規則

1、在DriverController中創建一個司機篩選方法,比如根據城市來篩選,在DriverController中創建一個方法:

/***
     * 搜素指定城市的司機
     */
@SentinelResource(value = "search")
@GetMapping(value = "/search/{city}")
public Driver search(@PathVariable(value = "city")String city){
    System.out.println("查詢的司機所在城市:"+city);
    //假設查詢到了一個司機信息
    Driver driver = new Driver();
    driver.setName("唐僧老師");
    driver.setId("No.1");
    return driver;
}

2、對熱門參數進行控制,對熱點數據執行特殊限流,比如資源參數列表中的第一個參數值為恩施的時候執行限流,在DriverApplication中創建限流配置,代碼如下:

/***
 * 熱點參數初始化
 */
@PostConstruct
private static void initParamFlowRules() {
    ParamFlowRule rule = new ParamFlowRule("search")
            //參數下標為0
            .setParamIdx(0)
            //限流模式為QPS
            .setGrade(RuleConstant.FLOW_GRADE_QPS)
            //統計視窗時間長度(單位為秒)
            .setDurationInSec(10)
            //流控效果(支持快速失敗和勻速排隊模式)
            //CONTROL_BEHAVIOR_DEFAULT:限流行為,直接拒絕
            //CONTROL_BEHAVIOR_WARM_UP:限流行為,勻速排隊
            //CONTROL_BEHAVIOR_RATE_LIMITER:限流行為,勻速排隊
            .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
            //最大排隊等待時長(僅在勻速排隊模式生效  CONTROL_BEHAVIOR_RATE_LIMITER)
            //.setMaxQueueingTimeMs(600)
            //最大閾值為5
            .setCount(5);

    // 為特定參數單獨設置規則
    //如下配置:當下標為0的參數值為恩施的時候,閾值到達2的時候則執行限流
    ParamFlowItem item = new ParamFlowItem()
            //參數類型為String類型
            .setClassType(String.class.getName())
            //設置閾值為2
            .setCount(2)
            //需要統計的值
            .setObject(String.valueOf("恩施"));
    rule.setParamFlowItemList(Collections.singletonList(item)); //返回的是不可變的集合,但是這個長度的集合只有1,可以減少記憶體空間
    //載入熱點數據
    ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}

2、我們訪問 http://localhost:18081/driver/search/天津/ 的時候,連續執行5次,才會限流,

我們訪問 http://localhost:18081/driver/search/恩施/ 的時候,連續執行2次,就會限流,

2.3 OpenFeign支持

Sentinel 適配了 Feign 組件。如果想使用,除了外還需要 2 個步驟:

1:引入 `spring-cloud-starter-alibaba-sentinel` 的依賴
2:加入 spring-cloud-starter-openfeign 依賴
3:配置文件打開 Sentinel 對 Feign 的支持:feign.sentinel.enabled=true

file

在上面案例中,我們可以實現用戶打車成功調用hailtaxi-order執行下單,並且通過feign調用hailtaxi-driver修改司機狀態,此時我們可以使用Sentinel實現Feign調用降級、限流。

註意:

在進行操作之前,可以先將hailtaxi-driver中的訪問控制規則註釋掉,註釋掉DriverApplication#initAuthorityRuleDriverApplication#initSystemRule上的註解即可

1、在hailtaxi-order中引入sentinelOpenFeign依賴,配置如下:

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

<!--Openfeign  api模塊中已存在也可不引入-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、在hailtaxi-order的配置文件中開啟Feign支持sentinel,配置如下:

feign:
  #開啟Sentinel對Feign的支持
  sentinel:
    enabled: true

註意:現在配置信息都存放在了nacos中,所以找到hailtaxi-order.yaml的Data ID配置,將以上配置添加進去!

file

註意修改完後一定要發佈才能生效!!!

3、因為hailtaxi-order要通過openfeign去調用hailtaxi-driver中的DriverController#status方法,改造一下該方法

/****
     * 更新司機信息
     */
@PutMapping(value = "/status/{id}/{status}")
public Driver status(@PathVariable(value = "id")String id,@PathVariable(value = "status")Integer status) throws Exception {
    log.info("當前服務占用的埠為:{}",port);
    //修改狀態
    driverService.update(id,status);
    //修改狀態後的司機信息
    Driver driver = driverService.findById(id);
    if (driver == null) {
        throw new RuntimeException("學生id="+id+",不存在");
    }
    return driver;
}

模擬被調用服務出現異常的情況

3、先驗證正確性,啟動hailtaxi-gatewayhailtaxi-orderhailtaxi-driver服務,使用postman訪問:

http://localhost:8001/order

4、為了測試程式異常能實現降級操作,我們在hailtaxi-order中將OrderInfoController.add()方法的司機ID改成一個不存在的司機ID,讓程式報錯,測試降級處理,代碼如下:

/***
     * 下單
     */
@PostMapping
public OrderInfo add(){
    //修改司機信息  司機ID=1
    Driver driver = driverFeign.status("3",2);// 改成一個不存在的id
    //創建訂單
    OrderInfo orderInfo = new OrderInfo("No"+((int)(Math.random()*10000)), (int)(Math.random()*100), new Date(), "深圳北站", "羅湖港", driver);
    orderInfoService.add(orderInfo);
    return orderInfo;
}

file

5、再次啟動測試:

file

註意:服務調用出錯要進行降級操作有兩個地方都可以進行,一是在被調用方進行降級,一是在調用方進行降級,

在被調用方進行降級前面已經講過了,所以接下來講解如何在調用方(openfeign)結合 sentinel 進行降級處理。

2.3.1 fallback

hailtaxi-api模塊中找到DriverFeign介面:

1、為Feign介面創建一個實現類:com.itheima.driver.feign.fallback.DriverFeignFallback,在實現類中處理程式異常降級處理方法,代碼如下:

package com.itheima.driver.feign.fallback;

import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import org.springframework.stereotype.Component;

@Component
public class DriverFeignFallback implements DriverFeign {

    /**
     * status()降級處理方法
     */
    @Override
    public Driver status(String id, Integer status) {
        Driver driver = new Driver();
        driver.setId(id);
        driver.setStatus(status);
        driver.setName("系統比較繁忙,請您稍後再試!");
        return driver;
    }
}

2、在DriverFeign介面上添加fallback屬性指定降級處理的類,代碼如下:

@FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)

註意:修改了hailtaxi-api模塊,最好cleanpackage!!

3、啟動運行,會發生如下問題:

java.lang.AbstractMethodError: com.alibaba.cloud.sentinel.feign.SentinelContractHolder.parseAndValidatateMetadata(Ljava/lang/Class;)Ljava/util/List;

出現上面問題的主要原因是當前SpringCloud版本存在問題。

Hoxton.SR1 中,fegin.context介面方法的定義為parseAndValidatateMetadata
Hoxton.SR3 中,fegin.context介面方法的定義為parseAndValidateMetadata

我們現在需要把Hoxton.SR1換成Hoxton.SR3,因此需要在hailtaxi-parent修改SpringCloud版本:

file

SpringCloud版本升級至Hoxton.SR3同時將SpringBoot版本升級至2.2.10,如上圖:

https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-Hoxton-Release-Notes

此時我們測試,效果如下:

file

2.3.2 fallbackFactory

我們可以為DriverFeign介面創建一個降級處理的工廠對象,在工廠對象中處理程式異常降級處理方法,用工廠對象處理可以拿到異常信息,代碼如下:

1、創建:com.itheima.driver.feign.fallback.DriverFeignFallbackFactory

package com.itheima.driver.feign.fallback;

import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class DriverFeignFallbackFactory implements FallbackFactory<DriverFeign> {
    @Override
    public DriverFeign create(Throwable throwable) {
        return new DriverFeign() {
            /**
             * status()降級處理方法
             */
            @Override
            public Driver status(String id, Integer status) {
                Driver driver = new Driver();
                driver.setId(id);
                driver.setStatus(status);
                driver.setName("系統比較繁忙,請您稍後再試!");
                return driver;
            }
        };
    }
}

2、在DriverFeign介面上添加fallbackFactory屬性指定講解處理的類,代碼如下:

@FeignClient(value = "hailtaxi-driver",fallbackFactory = DriverFeignFallbackFactory.class)

3、再次啟動測試,效果如下:

file

好了,接下來,咱麽來說一說Sentinel控制台

4 Sentinel控制台

Sentinel 控制台是流量控制、熔斷降級規則統一配置和管理的入口,它為用戶提供了機器自發現、簇點鏈路自發現、監控、規則配置等功能。在 Sentinel 控制臺上,我們可以配置規則並實時查看流量控制效果。

4.1 Sentinel控制台安裝

Sentinel 提供一個輕量級的開源控制台,它提供機器發現以及健康情況管理、監控(單機和集群),規則管理和推送的功能。

Sentinel 控制台包含如下功能:

  • 查看機器列表以及健康情況:收集 Sentinel 客戶端發送的心跳包,用於判斷機器是否線上。
  • 監控 (單機和集群聚合):通過 Sentinel 客戶端暴露的監控 API,定期拉取並且聚合應用監控信息,最終可以實現秒級的實時監控。
  • 規則管理和推送:統一管理推送規則。
  • 鑒權:生產環境中鑒權非常重要。這裡每個開發者需要根據自己的實際情況進行定製。

Sentinel控制台安裝可以基於jar包啟動的方式安裝,也可以基於docker安裝,為了方便操作,我們這裡採用docker安裝方式:

docker run --name=sentinel-dashboard -d -p 8858:8858 -d --restart=on-failure bladex/sentinel-dashboard:1.8.0

安裝好了後,我們可以直接訪問 http://192.168.200.129:8858/,預設用戶名和密碼都是 sentinel

file

登錄後,效果如下:

file

4.2 接入控制台

客戶端需要引入 Transport 模塊來與 Sentinel 控制台進行通信,可以通過 pom.xml 引入 JAR 包:

<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>x.y.z</version>
</dependency>

如果是SpringBoot工程接入Sentinel,可以直接引入如下依賴包:

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>

hailtaxi-gateway接入控制台:

1、引入依賴包:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

2、在核心配置文件中配置Sentinel控制台服務地址

spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: 192.168.200.129:8858

註意:

1、這裡的 spring.cloud.sentinel.transport.port 埠配置會在應用對應的機器上啟動一個 Http Server,該 Server 會與 Sentinel 控制台做交互,比如限流規則拉取。

2、配置信息現在是存儲到nacos中,故要在nacos中找到hailtaxi-gateway.yaml,進行如下配置

file

修改之後記得發佈!

3、啟動各個服務,

此時我們出發一些請求操作,再看Sentinel控制台會多一個服務監控:

file

4.3 可視化管理

4.3.1 實時監控

同一個服務下的所有機器的簇點信息會被彙總,並且秒級地展示在"實時監控"下。

註意: 實時監控僅存儲 5 分鐘以內的數據,如果需要持久化,需要通過調用實時監控介面來定製。

file

如果要獲取監控數據,直接調用 http://localhost:8719/clusterNode 即可獲取,效果如下:

file

4.3.2 流控規則

我們可以在【流控規則】頁面中新增,點擊【流控規則】進入頁面新增頁面,如下圖:

file

資源名:其實可以和請求路徑保持一致,這裡的流控模式為QPS,觸發流控執行閾值為1,流控模式為讓當前請求的資源快速直白。

這裡的參數和我們程式中的參數其實是一樣的,如下說明:

resource:資源名,即限流規則的作用對象
count: 限流閾值
grade: 限流閾值類型(QPS 或併發線程數)
limitApp: 流控針對的調用來源,若為 default 則不區分調用來源
strategy: 調用關係限流策略:直接,關聯,鏈路
controlBehavior: 流量控制效果(直接拒絕、Warm Up、勻速排隊)

我們測試效果如下:

file

4.3.3 降級規則

我們可以選擇降級規則>新增降級規則,如下圖:

1614185331968

降級規則的熔斷策略有3種,分別是慢調用比例、異常比例、異常數,和程式中是一樣的。

4.3.4 熱點數據

熱點即經常訪問的數據。很多時候我們希望統計某個熱點數據中訪問頻次最高的 Top K 數據,並對其訪問進行限制。

file

拓展知識點:

註意:如果流控模式是鏈路模式,需要引入如下依賴:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>1.8.0</version>
</dependency>

創建過濾器:

/***
 * CommonFilter過濾器
 * @return
 */
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new CommonFilter());
    registration.addUrlPatterns("/*");	//過濾所有請求
    // 入口資源關閉聚合
    registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
    registration.setName("sentinelFilter");
    registration.setOrder(1);
    return registration;
}

bootstrap.yml配置:web-context-unify: false

  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8858
      web-context-unify: false  #可根據不同的URL 進行鏈路限流

本文由傳智教育博學谷 - 狂野架構師教研團隊發佈
轉載請註明出處!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • KeyStore(譯:密鑰存儲庫) 代表用於加密密鑰和證書的存儲設施。 KeyStore 管理不同類型的 entry(譯:條目)。每種類型的 entry 都實現了 KeyStore.Entry 介面。提供了三個基本的 KeyStore.Entry 實現: KeyStore.PrivateKeyEnt ...
  • 下載任務類設計 /** * 下載任務執行器 * @author Shixy */ @Slf4j public class Downloader implements Runnable { private String filePath; private Callable<String> callab ...
  • vector是一個常被使用的容器,本文對如何使用它進行初步的介紹。 ...
  • mkcert(Windows環境) 1.下載地址:https://github.com/FiloSottile/mkcert/releases 2.選擇版本 3.以管理員身份運行`命令提示符 1) cd C:/ #進入工具存放的目錄下 2) mkcert-v1.4.4-windows-amd64.e ...
  • 在NumPy中,矩陣是 ndarray 的子類,與數學概念中的矩陣一樣,NumPy中的矩陣也是二維的,可以使用 mat 、 matrix 以及 bmat 函數來創建矩陣。 一、創建矩陣 mat 函數創建矩陣時,若輸入已為 matrix 或 ndarray 對象,則不會為它們創建副本。 因此,調用 m ...
  • 一個工作了6年的粉絲和我說, 最近面試感覺越來越難的,基本上都會問技術底層原理,甚至有些還會問到操作系統層面的知識。 我說,現在各個一線大廠有很多優秀的程式員畢業了,再加上市場大環境不好對程式員的需求量也在減少。 如果技術底子不好,確實找工作會很困難。 今天分享的問題是:”new String(“a ...
  • #所有流程 !!!不要修改為搶票,遵守法律法規是每一個中國公民應盡的責任,盜用並造成不良後果自負!!! ''' #可以實現買學生票,兒童票,殘疾票,成人票和所有能選的座位類別。 2022-05-22目前為止還是完全正常運行的,之後網頁改動就不一定了哦!!!!!!12306官網模擬買票:1.輸入賬號密 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...