主頁 > 後端開發 > Sentinel入門到實操 (限流熔斷降級)

Sentinel入門到實操 (限流熔斷降級)

2023-02-14 06:57:57 後端開發

微服務保護——Sentinel

介紹Sentinel

1.背景

Sentinel是阿里巴巴開源的一款微服務流量控制組件,官網地址:https://sentinelguard.io/zh-cn/index.html

Sentinel 具有以下特征:

?豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的范圍)、訊息削峰填谷、集群流量控制、實時熔斷下游不可用應用等,

?完備的實時監控:Sentinel 同時提供實時的監控功能,您可以在控制臺中看到接入應用的單臺機器秒級資料,甚至 500 臺以下規模的集群的匯總運行情況,

?廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合,您只需要引入相應的依賴并進行簡單的配置即可快速地接入 Sentinel,

?完善的 SPI 擴展點:Sentinel 提供簡單易用、完善的 SPI 擴展介面,您可以通過實作擴展介面來快速地定制邏輯,例如定制規則管理、適配動態資料源等,

2. 服務保護技術對比

在SpringCloud當中支持多種服務保護技術:

  • Netfix Hystrix
  • Sentinel
  • Resilience4J

早期比較流行的是Hystrix框架,但目前國內實用最廣泛的還是阿里巴巴的Sentinel框架,這里我們做下對比:

Sentinel Hystrix
隔離策略 信號量隔離 執行緒池隔離/信號量隔離
熔斷降級策略 基于慢呼叫比例或例外比例 基于失敗比率
實時指標實作 滑動視窗 滑動視窗(基于 RxJava)
規則配置 支持多種資料源 支持多種資料源
擴展性 多個擴展點 插件的形式
基于注解的支持 支持 支持
限流 基于 QPS,支持基于呼叫關系的限流 有限的支持
流量整形 支持慢啟動、勻速排隊模式 不支持
系統自適應保護 支持 不支持
控制臺 開箱即用,可配置規則、查看秒級監控、機器發現等 不完善
常見框架的適配 Servlet、Spring Cloud、Dubbo、gRPC 等 Servlet、Spring Cloud Netflix

3.安裝Sentinel

1)下載

sentinel官方提供了UI控制臺,方便我們對系統做限流設定,大家可以在GitHub下載,

課前資料也提供了下載好的jar包:

image

2)運行

將jar包放到任意非中文目錄,執行命令:

java -jar sentinel-dashboard-1.8.1.jar

如果要修改Sentinel的默認埠、賬戶、密碼,可以通過下列配置:

配置項 默認值 說明
server.port 8080 服務埠
sentinel.dashboard.auth.username sentinel 默認用戶名
sentinel.dashboard.auth.password sentinel 默認密碼

例如,修改埠:

java -Dserver.port=8090 -jar sentinel-dashboard-1.8.1.jar

3)訪問

訪問http://localhost:8080頁面,就可以看到sentinel的控制臺了:需要輸入賬號和密碼,默認都是:sentinel

image

登錄后,發現一片空白,什么都沒有:這是因為我們還沒有與微服務整合,

image

微服務整合Sentinel

四步驟:

0. 啟動Nacos

進入到nacos的bin檔案夾中cmd:startup.cmd -m standalone

1. 依賴

在指定微服務中匯入該依賴

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

2. yaml組態檔

修改application.yaml檔案,添加下面內容:

server:
  port: 8088 #微服務地址
spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8080 #sentinel控制臺訪問地址

3. 訪問任意介面

打開瀏覽器,訪問任意介面 如:http://localhost:8088/order/101,這樣才能觸發sentinel的監控,

然后再訪問sentinel的控制臺,查看效果:

image

FeignClient整合Sentinel

整合后撰寫失敗降級邏輯:就是請求失敗后不是直接回傳一個例外而是回傳一個空物件(保證用戶體驗)

SpringCloud中,微服務呼叫都是通過Feign來實作的,因此做客戶端保護必須整合Feign和Sentinel,

1. 修改組態檔

保證已經有了Feign和sentinel依賴

修改OrderService的application.yml檔案,開啟Feign的Sentinel功能:

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

2. 撰寫請求失敗降級邏輯

業務失敗后,不能直接報錯,而應該回傳用戶一個友好提示或者默認結果,這個就是失敗降級邏輯,

給FeignClient撰寫請求失敗后的降級邏輯

①方式一:FallbackClass,無法對遠程呼叫的例外做處理【不推薦】

②方式二:FallbackFactory,可以對遠程呼叫的例外做處理【推薦】

這里我們演示方式二的請求失敗降級處理,

步驟一:在feing-api專案中定義類,實作FallbackFactory:

image

代碼:

package cn.itcast.feign.clients.fallback;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() { //前提是必須有UserClient類和findById方法
            @Override
            public User findById(Long id) {
                log.error("查詢用戶例外", throwable);
                return new User();
            }
        };
    }
}

步驟二:在feing-api專案中的DefaultFeignConfiguration類中將UserClientFallbackFactory注冊為一個Bean:

記得配置類需要@Component

@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
    return new UserClientFallbackFactory();
}

步驟三:在feing-api專案中的UserClient介面中使用UserClientFallbackFactory:

import cn.itcast.feign.clients.fallback.UserClientFallbackFactory;
import cn.itcast.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "https://www.cnblogs.com/buchizicai/p/userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

重啟后,訪問一次訂單查詢業務,然后查看sentinel控制臺,可以看到新的簇點鏈路:

image

雪崩問題

白話:雪崩問題是微服務之間相互呼叫,因為呼叫鏈中的一個服務故障,引起整個鏈路都無法訪問的情況,

服務A和其他服務需要服務D回應,但此時服務D故障了,服務A無法接收到結果,由于服務器支持的執行緒和并發數有限,請求一致阻塞會導致服務器資源耗盡,從而導致依賴于當前服務的其它服務隨著時間的推移,最終也都會變的不可用,形成級聯失敗,雪崩就發生了

與服務D執行緒有關的服務雪崩:

服務D故障——> 服務A等有關服務阻塞

與服務D執行緒無關服務雪崩:

服務D故障——> 大量請求阻塞 ——> 服務器資源耗盡 ——>其他服務變得不可用 ——> 級聯失敗(雪崩)

如果服務提供者I發生了故障,當前的應用的部分業務因為依賴于服務I,因此也會被阻塞,此時,其它不依賴于服務I的業務似乎不受影響,但是,依賴服務I的業務請求被阻塞,用戶不會得到回應,則tomcat的這個執行緒不會釋放,于是越來越多的用戶請求到來,越來越多的執行緒會阻塞,服務器支持的執行緒和并發數有限,請求一直阻塞,會導致服務器資源耗盡,從而導致所有其它服務都不可用,那么當前服務也就不可用了,那么,依賴于當前服務的其它服務隨著時間的推移,最終也都會變的不可用,形成級聯失敗,雪崩就發生了

image

解決方案

限流是對服務的保護,避免因瞬間高并發流量而導致服務故障,進而避免雪崩,是一種預防措施,

超時處理、執行緒隔離、降級熔斷是在部分服務故障時,將故障控制在一定范圍,避免雪崩,是一種補救措施,

1. 預防措施

1.1 限流

流量控制:限制業務訪問的QPS,避免服務因流量的突增而故障,

image

2. 補救措施

2.1 超時處理

超時處理:設定超時時間,請求超過一定時間沒有回應就回傳錯誤資訊,不會無休止等待

image

2.2 艙壁模式

艙壁模式來源于船艙的設計:船艙都會被隔板分離為多個獨立空間,當船體破損時,只會導致部分空間進入,將故障控制在一定范圍內,避免整個船體都被淹沒,

image

我們可以限定每個業務能使用的執行緒數,避免耗盡整個tomcat的資源,因此也叫執行緒隔離,

image

2.3 斷路器

斷路器模式:由斷路器統計業務執行的例外比例,如果超出閾值則會熔斷該業務,攔截訪問該業務的一切請求,

斷路器會統計訪問某個服務的請求數量,例外比例:

image

當發現訪問服務D的請求例外比例過高時,認為服務D有導致雪崩的風險,會攔截訪問服務D的一切請求,形成熔斷:

image

限流:流量控制

1. 簇點鏈路

當請求進入微服務時,首先會訪問DispatcherServlet,然后進入Controller、Service、Mapper,這樣的一個呼叫鏈就叫做簇點鏈路,簇點鏈路中被監控的每一個介面就是一個資源

默認情況下sentinel會監控SpringMVC的每一個端點(Endpoint,也就是controller中的方法),因此SpringMVC的每一個端點(Endpoint)就是呼叫鏈路中的一個資源,

例如,我們剛才訪問的order-service中的OrderController中的端點:/order/

image

流控、熔斷等都是針對簇點鏈路中的資源來設定的,因此我們可以點擊對應資源后面的按鈕來設定規則:

  • 流控:流量控制
  • 降級:降級熔斷
  • 熱點:熱點引數限流,是限流的一種
  • 授權:請求的權限控制

2. 流控模式

QPS是每秒請求數

流控模式有哪些?

?直接:對當前資源限流

?關聯:高優先級資源觸發閾值,對低優先級資源限流,

?鏈路:閾值統計時,只統計從指定資源進入當前資源的請求,是對請求來源的限流

2.1 直接模式

直接模式:請求訪問介面在每秒內只能通過n個請求(n是單機閾值),其他請求會報錯429:被限流

配置規則:

image

2.2 關聯模式

需要對哪個介面限流就對哪個介面(端點)設定流控

使用場景:比如用戶支付時需要修改訂單狀態,同時用戶要查詢訂單,查詢和修改操作會爭搶資料庫鎖,產生競爭,業務需求是優先支付和更新訂單的業務,因此當修改訂單業務觸發閾值時,需要對查詢訂單業務限流,

滿足以下條件可以使用關聯模式:

  • 兩個有競爭關系的資源
  • 一個優先級較高,一個優先級較低

關聯模式:統計與當前資源相關的另一個資源(相不相關人為說了算),觸發閾值時,對當前資源限流

配置規則

image

語法說明:當/write資源訪問量觸發閾值n時,就會對/read資源限流,避免影響/write資源,

舉例:

需求說明

  • 在OrderController新建兩個端點:/order/query和/order/update,無需實作業務

  • 配置流控規則,當/order/ update資源被訪問的QPS超過5時,對/order/query請求限流

1)定義/order/query端點,模擬訂單查詢

@GetMapping("/query")
public String queryOrder() {
    return "查詢訂單成功";
}

2)定義/order/update端點,模擬訂單更新

@GetMapping("/update")
public String updateOrder() {
    return "更新訂單成功";
}

重啟微服務并訪問介面,查看sentinel控制臺的簇點鏈路:

image

3)配置流控規則

對哪個端點限流,就點擊哪個端點后面的按鈕,我們是對訂單查詢/order/query限流,因此點擊它后面的按鈕:

image

在表單中填寫流控規則:

image

4)在Jmeter測驗

選擇《流控模式-關聯》:

image

可以看到1000個用戶,100秒,因此QPS為10,超過了我們設定的閾值:5

查看http請求:

image

請求的目標是/order/update,這樣這個斷點就會觸發閾值,

但限流的目標是/order/query,我們在瀏覽器訪問,可以發現:確實被限流了,

image

2.3鏈路模式

鏈路模式:只針對從指定鏈路訪問到本資源的請求做統計,判斷是否超過閾值,

配置示例

例如有兩條請求鏈路:

  • /test1 --> /common
  • /test2 --> /common

test1和test2是介面,common一般是方法,判斷從這個介面訪問這個方法的單機閾值是否到達QPS,超過則限制

如果只希望統計從/test2進入到/common的請求,則可以這樣配置:

image

舉例:

需求:有查詢訂單和創建訂單業務,兩者都需要查詢商品,針對從查詢訂單進入到查詢商品的請求統計,并設定限流,

步驟:

  1. 在OrderService中添加一個queryGoods方法,不用實作業務

  2. 在OrderController中,改造/order/query端點,呼叫OrderService中的queryGoods方法

  3. 在OrderController中添加一個/order/save的端點,呼叫OrderService的queryGoods方法

  4. 給queryGoods設定限流規則,從/order/query進入queryGoods的方法限制QPS必須小于2

實作:

1)添加查詢商品方法

在order-service服務中,給OrderService類添加一個queryGoods方法:

public void queryGoods(){
    System.err.println("查詢商品");
}

2)查詢訂單時,查詢商品

在order-service的OrderController中,修改/order/query端點的業務邏輯:

@GetMapping("/query")
public String queryOrder() {
    // 查詢商品
    orderService.queryGoods();
    // 查詢訂單
    System.out.println("查詢訂單");
    return "查詢訂單成功";
}

3)新增訂單,查詢商品

在order-service的OrderController中,修改/order/save端點,模擬新增訂單:

@GetMapping("/save")
public String saveOrder() {
    // 查詢商品
    orderService.queryGoods();
    // 查詢訂單
    System.err.println("新增訂單");
    return "新增訂單成功";
}

4)給查詢商品添加資源標記

默認情況下,OrderService中的方法是不被Sentinel監控的,需要我們自己通過注解來標記要監控的方法,

給OrderService的queryGoods方法添加@SentinelResource注解:

@SentinelResource("goods")
public void queryGoods(){
    System.err.println("查詢商品");
}

鏈路模式中,是對不同來源的兩個鏈路做監控,但是sentinel默認會給進入SpringMVC的所有請求設定同一個root資源,會導致鏈路模式失效,

我們需要關閉這種對SpringMVC的資源聚合,修改order-service服務的application.yml檔案:

spring:
  cloud:
    sentinel:
      web-context-unify: false # 關閉context整合

重啟服務,訪問/order/query和/order/save,可以查看到sentinel的簇點鏈路規則中,出現了新的資源:

image

5)添加流控規則

點擊goods資源后面的流控按鈕,在彈出的表單中填寫下面資訊:只統計從/order/query進入/goods的資源,QPS閾值為2,超出則被限流,

image

6)Jmeter測驗

選擇《流控模式-鏈路》:

image

可以看到這里200個用戶,50秒內發完,QPS為4,超過了我們設定的閾值2

一個http請求是訪問/order/save:

image

運行的結果:完全不受影響,

image

另一個是訪問/order/query:

image

運行結果:每次只有2個通過,

image

3. 流控效果

  • 快速失敗:QPS超過閾值時,拒絕新的請求

  • warm up: QPS超過閾值時,拒絕新的請求;QPS閾值是逐漸提升的,可以避免冷啟動時高并發導致服務宕機,

  • 排隊等待:請求會進入佇列,按照閾值允許的時間間隔依次執行請求;如果請求預期等待時長大于超時時間,直接拒絕

在流控的高級選項中,還有一個流控效果選項:

image

流控效果是指請求達到流控閾值時應該采取的措施,包括三種:

  • 快速失敗:達到閾值后,新的請求會被立即拒絕并拋出FlowException例外,是默認的處理方式,

  • warm up:預熱模式,對超出閾值的請求同樣是拒絕并拋出例外,但這種模式閾值會動態變化,從一個較小值逐漸增加到最大閾值,

  • 排隊等待(勻速器):讓所有的請求按照先后次序排隊執行,兩個請求的間隔不能小于指定時長

    勻速排隊,讓請求以勻速的速度通過,閾值型別必須設定為QPS,否則無效

3.1 快速失敗

達到閾值后,新的請求會被立即拒絕并拋出FlowException例外,是默認的處理方式,

3.2 warm up

閾值一般是一個微服務能承擔的最大QPS,但是一個服務剛剛啟動時,一切資源尚未初始化(冷啟動),如果直接將QPS跑到最大值,可能導致服務瞬間宕機,

warm up也叫預熱模式,是應對服務冷啟動的一種方案,請求閾值初始值是 maxThreshold / coldFactor,持續指定時長后,逐漸提高到maxThreshold值,而coldFactor的默認值是3

例如,我設定QPS的maxThreshold為10,預熱時間為5秒,那么初始閾值就是 10 / 3 ,也就是3,然后在5秒后逐漸增長到10.

image

舉例:

需求:給/order/{orderId}這個資源設定限流,最大QPS為10,利用warm up效果,預熱時長為5秒

1)配置流控規則:

image

2)Jmeter測驗

選擇《流控效果,warm up》:

image

QPS為10.

剛剛啟動時,大部分請求失敗,成功的只有3個,說明QPS被限定在3:

image

隨著時間推移,成功比例越來越高:

image

到Sentinel控制臺查看實時監控:

image

一段時間后:

image

3.3 排隊等待

當請求超過QPS閾值時,快速失敗和warm up 會拒絕新的請求并拋出例外,

而排隊等待則是讓所有請求進入一個佇列中,然后按照閾值允許的時間間隔依次執行,后來的請求必須等待前面執行完成,如果請求預期的等待時間超出最大時長,則會被拒絕,

作業原理

例如:QPS = 5,意味著每200ms處理一個佇列中的請求;timeout = 2000,意味著預期等待時長超過2000ms的請求會被拒絕并拋出例外,

那什么叫做預期等待時長呢?

比如現在一下子同時來了12 個請求,因為每200ms執行一個請求,那么:

  • 第6個請求的預期等待時長 = 200 * (6 - 1) = 1000ms
  • 第12個請求的預期等待時長 = 200 * (12-1) = 2200ms

現在,第1秒同時接收到10個請求,但第2秒只有1個請求,此時QPS的曲線這樣的:

image

如果使用佇列模式做流控,所有進入的請求都要排隊,以固定的200ms的間隔執行,QPS會變的很平滑:

image

平滑的QPS曲線,對于服務器來說是更友好的,

舉例:

需求:給/order/{orderId}這個資源設定限流,最大QPS為10,利用排隊的流控效果,超時時長設定為5s

1)添加流控規則

image

2)Jmeter測驗

選擇《流控效果,佇列》:

image

QPS為15,已經超過了我們設定的10,

如果是之前的 快速失敗、warmup模式,超出的請求應該會直接報錯,

但是我們看看佇列模式的運行結果:

image

全部都通過了,

再去sentinel查看實時監控的QPS曲線:

image

QPS非常平滑,一致保持在10,但是超出的請求沒有被拒絕,而是放入佇列,因此回應時間(等待時間)會越來越長,

當佇列滿了以后,才會有部分請求失敗:

image

限流 :熱點引數限流

之前的限流是統計訪問某個資源的所有請求,判斷是否超過QPS閾值,而熱點引數限流是分別統計引數值相同的請求,判斷是否超過QPS閾值,

1. 全域引數限流

例如,一個根據id查詢商品的介面:

image

訪問/goods/{id}的請求中,id引數值會有變化,熱點引數限流會根據引數值分別統計QPS,統計結果:

image

當id=1的請求觸發閾值被限流時,id值不為1的請求不受影響,

配置示例:

image

解釋:對hot這個資源的0號引數(第一個引數)做統計,每1秒相同引數值的請求數不能超過5

2. 熱點引數限流

剛才的配置中,對查詢商品這個介面的所有商品一視同仁,QPS都限定為5.

而在實際開發中,可能部分商品是熱點商品,例如秒殺商品,我們希望這部分商品的QPS限制與其它商品不一樣,高一些,那就需要配置熱點引數限流的高級選項了:

image

結合上一個配置,這里的含義是對0號的long型別引數限流,每1秒相同引數的QPS不能超過5,有兩個例外:

?如果引數值是100,則每1秒允許的QPS為10

?如果引數值是101,則每1秒允許的QPS為15

案例

案例需求:給/order/{orderId}這個資源添加熱點引數限流,規則如下:

?默認的熱點引數規則是每1秒請求量不超過2

?給102這個引數設定例外:每1秒請求量不超過4

?給103這個引數設定例外:每1秒請求量不超過10

注意事項:熱點引數限流對默認的SpringMVC資源無效,需要利用@SentinelResource注解標記資源

1)標記資源

給order-service中的OrderController中的/order/{orderId}資源添加注解:

image

2)熱點引數限流規則

訪問該介面,可以看到我們標記的hot資源出現了:

image

這里不要點擊hot后面的按鈕,頁面有BUG

點擊左側選單中熱點規則選單:

image

點擊新增,填寫表單:

image

3)Jmeter測驗

選擇《熱點引數限流 QPS1》:

image

這里發起請求的QPS為5.

包含3個http請求:

普通引數,QPS閾值為2

image

運行結果:

image

例外項,QPS閾值為4

image

運行結果:

image

例外項,QPS閾值為10

image

運行結果:

image

艙壁模式:執行緒隔離

執行緒隔離建議設定監控介面里的遠程呼叫,因為一旦發生熔斷和隔離是不允許外界訪問該介面,監控遠程呼叫是因為遠程呼叫使用的feign-api模塊對遠程呼叫介面方法寫了發生熔斷和隔離時回傳空物件,如果監控外部介面,一旦發生隔離則直接報錯,阻止用戶訪問介面并不會回傳空物件(因為該介面方法沒寫發生熔斷和隔離時回傳空物件),

1. 執行緒隔離的兩種方式

執行緒隔離有兩種方式實作:

區別:

信號量——高扇出(高并發) 執行緒池——底扇出(請求量小)

特點:

信號量隔離——基于計數器模式,簡單,開銷小

執行緒池隔離是——基于執行緒池模式,有額外開銷,但隔離控制更強

  • 執行緒池隔離

  • 信號量隔離(Sentinel默認采用,選擇QPS)

如圖:

image

執行緒池隔離:給每個服務呼叫業務分配一個執行緒池,利用執行緒池本身實作隔離效果

信號量隔離:不創建執行緒池,而是計數器模式,記錄業務使用的執行緒數量,達到信號量上限時,禁止新的請求,

兩者的優缺點

image

2. sentinel的執行緒隔離

用法說明:

在添加限流規則時,可以選擇兩種閾值型別:

image

  • QPS:就是每秒的請求數,在快速入門中已經演示過

  • 執行緒數:是該資源能使用用的tomcat執行緒數的最大值,也就是通過限制執行緒數量,實作執行緒隔離(艙壁模式),

案例:

案例需求:給 order-service服務中的UserClient的查詢用戶介面設定流控規則,執行緒數不能超過 2,然后利用jemeter測驗,

1)配置隔離規則

選擇feign介面后面的流控按鈕:

image

填寫表單:

image

2)Jmeter測驗

選擇《閾值型別-執行緒數<2》:

image

一次發生10個請求,有較大概率并發執行緒數超過2,而超出的請求會走之前定義的失敗降級邏輯,

查看運行結果:

image

發現雖然結果都是通過了,不過部分請求得到的回應是降級回傳的null資訊,

路斷器:熔斷降級

熔斷建議設定監控介面里的遠程呼叫,因為一旦發生熔斷和隔離是不允許外界訪問該介面,監控遠程呼叫是因為遠程呼叫使用的feign-api模塊對遠程呼叫介面方法寫了發生熔斷和隔離時回傳空物件,如果監控外部介面,一旦發生熔斷則直接報錯,阻止用戶訪問介面并不會回傳空物件(因為該介面方法沒寫發生熔斷和隔離時回傳空物件),

熔斷降級是解決雪崩問題的重要手段,其思路是由斷路器統計服務呼叫的例外比例、慢請求比例,如果超出閾值則會熔斷該服務,即攔截訪問該服務的一切請求;而當服務恢復時,斷路器會放行訪問該服務的請求,

斷路器控制熔斷和放行是通過狀態機來完成的:

image

狀態機包括三個狀態:

  • closed:關閉狀態,斷路器放行所有請求,并開始統計例外比例、慢請求比例,超過閾值則切換到open狀態
  • open:打開狀態,服務呼叫被熔斷,訪問被熔斷服務的請求會被拒絕,快速失敗,直接走降級邏輯,Open狀態5秒后會進入half-open狀態
  • half-open:半開狀態,放行一次請求,根據執行結果來判斷接下來的操作,
    • 請求成功:則切換到closed狀態
    • 請求失敗:則切換到open狀態

斷路器熔斷策略有三種:慢呼叫、例外比例、例外數

1. 熔斷策略一:慢呼叫

慢呼叫:業務的回應時長(RT)大于指定時長的請求認定為慢呼叫請求,在指定時間內,如果請求數量超過設定的最小數量,慢呼叫比例大于設定的閾值,則觸發熔斷,

例如:

image

解讀:RT超過500ms的呼叫是慢呼叫,統計最近10000ms內的請求,如果請求量超過10次,并且慢呼叫比例不低于0.5,則觸發熔斷,熔斷時長為5秒,然后進入half-open狀態,放行一次請求做測驗,

案例:

需求:給 UserClient的查詢用戶介面設定降級規則,慢呼叫的RT閾值為50ms,統計時間為1秒,最小請求數量為5,失敗閾值比例為0.4,熔斷時長為5

1)設定慢呼叫

修改user-service中的/user/{id}這個介面的業務,通過休眠模擬一個延遲時間:

image

此時,orderId=101的訂單,關聯的是id為1的用戶,呼叫時長為60ms:

image

orderId=102的訂單,關聯的是id為2的用戶,呼叫時長為非常短;

image

2)設定熔斷規則

下面,給feign介面設定降級規則:

image

規則:

image

超過50ms的請求都會被認為是慢請求

3)測驗

在瀏覽器訪問:http://localhost:8088/order/101,快速重繪5次后,可以發現:觸發了熔斷,請求時長縮短至5ms,快速失敗了,并且走降級邏輯,回傳的null

這里呼叫order/101是因為,這個介面里面會呼叫user/101,所以還是會觸發前面設定的熔斷規則

image

在瀏覽器訪問:http://localhost:8088/order/102,竟然也被熔斷了:

image

2. 熔斷策略二和三:例外比例、例外數

例外比例或例外數:統計指定時間內的呼叫,如果呼叫次數超過指定請求數,并且出現例外的比例達到設定的比例閾值(或超過指定例外數),則觸發熔斷,

例如,一個例外比例設定:

image

解讀:統計最近1000ms內的請求,如果請求量超過10次,并且例外比例不低于0.4,則觸發熔斷,

一個例外數設定:

image

解讀:統計最近1000ms內的請求,如果請求量超過10次,并且例外比例不低于2次,則觸發熔斷,

案例

需求:給 UserClient的查詢用戶介面設定降級規則,統計時間為1秒,最小請求數量為5,失敗閾值比例為0.4,熔斷時長為5s

1)設定例外請求

首先,修改user-service中的/user/{id}這個介面的業務,手動拋出例外,以觸發例外比例的熔斷:也就是說,id 為 2時,就會觸發例外

image

2)設定熔斷規則

下面,給feign介面設定降級規則:

image

規則:在5次請求中,只要例外比例超過0.4,也就是有2次以上的例外,就會觸發熔斷,

image

3)測驗

在瀏覽器快速訪問:http://localhost:8088/order/102,快速重繪5次,觸發熔斷:

image

此時,我們去訪問本來應該正常的103:

image

授權

授權規則可以對請求方來源做判斷和控制,(通過判斷請求方的請求頭是否攜帶指定的引數來判斷)

1. sentinel授權介紹

授權規則可以對呼叫方的來源做控制,有白名單和黑名單兩種方式,

  • 白名單:來源(origin)在白名單內的呼叫者允許訪問

  • 黑名單:來源(origin)在黑名單內的呼叫者不允許訪問

點擊左側選單的授權,可以看到授權規則:

image

  • 資源名:就是受保護的資源,例如/order/

  • 流控應用:是來源者的名單,

    • 如果是勾選白名單,則名單中的來源被許可訪問,
    • 如果是勾選黑名單,則名單中的來源被禁止訪問,

比如:

image

我們允許請求從gateway到order-service,不允許瀏覽器訪問order-service,那么白名單中就要填寫網關的來源名稱(origin)

如何得到origin呢?

Sentinel是通過RequestOriginParser這個介面的parseOrigin來獲取請求的來源的,

public interface RequestOriginParser {
    /**
     * 從請求request物件中獲取origin,獲取方式自定義
     */
    String parseOrigin(HttpServletRequest request);
}

這個方法的作用就是從request物件中,獲取請求者的origin值并回傳,

默認情況下,sentinel不管請求者從哪里來,回傳值永遠是default,也就是說一切請求的來源都被認為是一樣的值default,

因此,我們需要自定義這個介面的實作,讓不同的請求,回傳不同的origin

2. sentinel設定授權

2.1 給網關添加請求頭

既然獲取請求origin的方式是從reques-header中獲取origin值,我們必須讓所有從gateway路由到微服務的請求都帶上origin頭

這個需要利用之前學習的一個GatewayFilter來實作,AddRequestHeaderGatewayFilter,

修改gateway服務中的application.yml,添加一個defaultFilter:

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway #逗號前是key,后面是value
      routes:
       # ...略

這樣,從gateway路由的所有請求都會帶上origin頭,值為gateway,而從其它地方到達微服務的請求則沒有這個頭,

2.2 獲取請求的origin

例如order-service服務中,我們定義一個RequestOriginParser的實作類:我們會嘗試從request-header中獲取origin值,

package cn.itcast.order.sentinel;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

@Component
public class HeaderOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 1.獲取請求頭
        String origin = request.getHeader("origin");
        // 2.非空判斷
        if (StringUtils.isEmpty(origin)) {
            origin = "blank";
        }
        return origin;
    }
}

2.3 sentinel操作

我們添加一個授權規則,放行origin值為gateway的請求,

image

配置如下:

image

現在,我們直接跳過網關,訪問order-service服務:

image

通過網關訪問:

image

自定義例外結果

默認情況下,發生限流、降級、授權攔截時,都會拋出例外到呼叫方,例外結果都是flow limmiting(限流),這樣不夠友好,無法得知是限流還是降級還是授權攔截,

1.例外型別

而如果要自定義例外時的回傳結果,需要實作BlockExceptionHandler介面:

public interface BlockExceptionHandler {
    /**
     * 處理請求被限流、降級、授權攔截時拋出的例外:BlockException
     */
    void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}

這個方法有三個引數:

  • HttpServletRequest request:request物件
  • HttpServletResponse response:response物件
  • BlockException e:被sentinel攔截時拋出的例外

這里的BlockException包含多個不同的子類:

例外 說明
FlowException 限流例外
ParamFlowException 熱點引數限流的例外
DegradeException 降級例外
AuthorityException 授權規則例外
SystemBlockException 系統規則例外

2.自定義例外處理

下面,我們就在order-service定義一個自定義例外處理類:

package cn.itcast.order.sentinel;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg = "未知例外";
        int status = 429;

        if (e instanceof FlowException) {
            msg = "請求被限流了";
        } else if (e instanceof ParamFlowException) {
            msg = "請求被熱點引數限流";
        } else if (e instanceof DegradeException) {
            msg = "請求被降級了";
        } else if (e instanceof AuthorityException) {
            msg = "沒有權限訪問";
            status = 401;
        }

        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
    }
}

重啟測驗,在不同場景下,會回傳不同的例外訊息.

限流:

image

授權攔截時:

image

sentinel規則持久化

sentinel的所有規則都是記憶體存盤,重啟后所有規則都會丟失,在生產環境下,我們必須確保這些規則的持久化,避免丟失,

1.規則管理模式

規則是否能持久化,取決于規則管理模式,sentinel支持三種規則管理模式:

  • 原始模式:Sentinel的默認模式,將規則保存在記憶體,重啟服務會丟失,
  • pull模式(存盤各服務器本地,一定時間內會輪詢檢查規則并更新)
  • push模式(存盤在nacos注冊中心)【推薦】

2. pull模式

pull模式:控制臺將配置的規則推送到Sentinel客戶端,而客戶端會將配置規則保存在本地檔案或資料庫中,以后會定時去本地檔案或資料庫中查詢,更新本地規則,

image

3. push模式

push模式:控制臺將配置規則推送到遠程配置中心,例如Nacos,Sentinel客戶端監聽Nacos,獲取配置變更的推送訊息,完成本地配置更新,

image

4. 實作push模式

4.1 引入依賴

在order-service中引入sentinel監聽nacos的依賴:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

4.2 配置nacos地址

在order-service中的application.yml檔案配置nacos地址及監聽的配置資訊:

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 # nacos地址
            dataId: orderservice-flow-rules #該組態檔的名稱
            groupId: SENTINEL_GROUP #該組態檔所在組的名稱
            rule-type: flow # 還可以是:degrade、authority、param-flow
        flow:
          nacos:
            server-addr: localhost:8848 # nacos地址
            dataId: orderservice-degrade-rules #該組態檔的名稱
            groupId: SENTINEL_GROUP #該組態檔所在組的名稱
            rule-type: degrade # 還可以是:degrade、authority、param-flow
        #... 可以多個flow根據需求設定

4.3 修改sentinel原始碼

一般不這樣修改太麻煩了,直接去網上找別人改好的

SentinelDashboard默認不支持nacos的持久化,需要修改原始碼,

4.3.1 解壓

解壓課前資料中的sentinel原始碼包:

image

然后并用IDEA打開這個專案,結構如下:

image

4.3.2 修改nacos依賴

在sentinel-dashboard原始碼的pom檔案中,nacos的依賴默認的scope是test,只能在測驗時使用,這里要去除:

image

將sentinel-datasource-nacos依賴的scope去掉:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
4.3.3 添加nacos支持

在sentinel-dashboard的test包下,已經撰寫了對nacos的支持,我們需要將其拷貝到main下,

image

4.3.4 修改nacos地址

然后,還需要修改測驗代碼中的NacosConfig類:

image

修改其中的nacos地址,讓其讀取application.properties中的配置:

image

在sentinel-dashboard的application.properties中添加nacos地址配置:

nacos.addr=localhost:8848
4.3.5 配置nacos資料源

另外,還需要修改com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2類:

image

讓我們添加的Nacos資料源生效:

image

4.3.6 修改前端頁面

接下來,還要修改前端頁面,添加一個支持nacos的選單,

修改src/main/webapp/resources/app/scripts/directives/sidebar/目錄下的sidebar.html檔案:

image

將其中的這部分注釋打開:

image

修改其中的文本:

image

4.3.7 重新編譯、打包專案

運行IDEA中的maven插件,編譯和打包修改好的Sentinel-Dashboard:

image

4.3.8 啟動

啟動方式跟官方一樣:

java -jar sentinel-dashboard.jar

如果要修改nacos地址,需要添加引數:

java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar

本文來自博客園,作者:不吃紫菜,遵循CC 4.0 BY-SA著作權協議,轉載請附上原文出處鏈接:https://www.cnblogs.com/buchizicai/p/17093746.html及本宣告,

本文著作權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543715.html

標籤:Java

上一篇:JAVA - - - String, StringBuffer,StringBuilder的區別

下一篇:Maven依賴管理

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more