天天看點

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

SpringCloud(11)— 微服務保護(Sentinel)

一 認識Sentinel

1.雪崩問題及其解決方案

微服務調用鍊路中的某個服務出現問題,引起整個鍊路中所有的微服務都不可用,這就是我們常說的雪崩問題。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

如何解決雪崩問題?

常見的解決方案有如下幾種:

  • 逾時處理:設定逾時時間,請求超過一定時間沒有響應就傳回錯誤資訊,防止無休止等待。
  • 艙壁模式:限定每個業務使用的線程數,避免耗盡整個web伺服器資源,是以也叫線程隔離。
  • 熔斷降級:由斷路器統計業務失敗的異常比例,如果超出門檻值則會熔斷該業務,攔截通路該業務的一切請求
  • 流量控制:限制通路業務的QPS(機關時間内的業務數量),避免服務因流量的突增而故障

2.微服務保護技術對比

sentinel目前是SpringBoot Alibaba的一個元件。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

3.認識Sentinel

Sentinel是阿裡巴巴的一款微服務流量控制元件。

官方網址:home | Sentinel (sentinelguard.io)

Github位址:GitHub - alibaba/Sentinel: A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向雲原生微服務的高可用流控防護元件)

Sentinel的特征:

  • 豐富的應用場景
  • 完備的實時監控
  • 廣泛的開源生态
  • 完善的SPI擴充點

4.安裝和運作Sentinel

Sentinel控制台 的下載下傳位址:Releases · alibaba/Sentinel (github.com)

下載下傳下來之後是一個 jar 包,使用指令運作即可,筆者這裡下載下傳的是最新版1.8.6

# 直接運作,預設端口為8080
java -jar sentinel-dashboard-1.8.6.jar

# 指定部分參數運作,通過 -Dserver.port=xxxx 指定運作端口
java -Dserver.port=8080 -jar sentinel-dashboard-1.8.6.jar

# 其餘更多配置參數參考官網
           

運作之後,在浏覽器通路即可。預設的使用者名和密碼均為 sentinel

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

常用的參數配置項:

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)
使用舉例:運作時在參數向前面加上“ -D ”,例如 -Dserver.port=xxxx 即可。

5.項目整合Sentinel

1.引入Sentinel 的依賴

<!--引入Sentinel依賴-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
           

2.在配置檔案中配置Sentinel相關參數

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
           

3.通路微服務的任意端口,觸發 sentinel 監控

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

二 流量控制

1.限流基本用法

簇點鍊路:指的是項目内部的調用鍊路,鍊路中被監控的每個接口就是一個資源。

預設情況下,sentinel 會監控微服務中的每一個端點(endpoint),是以微服務中的每一個端點就是調用鍊路中的一個資源

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

流控,熔斷

等都是

針對簇點鍊路中的資源

來設定的,可以直接點選資源後邊的按鈕來設定規則

1.流控設定

點選資源後面的

流控

按鈕,便可以對資源進行流控設定

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)
  • 資源名稱:表示要流控的資源
  • 針對來源:表示從什麼地方進來的請求需要進行流控,

    default

    表示一切對該資源的請求都要進行流控
  • 門檻值類型:

    QPS

    表示機關時間内的并發量,後邊的

    單機門檻值

    表示所選門檻值類型的上限。如果超出則會被攔截并且報錯

利用

jmeter

進行測試,會發現超出設定的

QPS

單機門檻值的請求将傳回被sentinel攔截的資訊

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)
SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

2.流控模式

現在來看看流控規則中的額進階設定

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

在流控規則界面打開

進階選線

按鈕,可以看到有很多

流控模式

流控效果

供我們選擇

其中

流控模式

中的三種模式代表的含義分别如下:

  • 直接:統計目前資源的請求,出發門檻值時對目前資源直接限流,也是預設的模式
  • 關聯:統計與目前資源關聯的另一個資源,觸發門檻值時,對目前資源限流
  • 鍊路:統計從指定鍊路通路到本資源的請求,觸發門檻值時,對

    指定鍊路

    限流
Sentinel中的流控設定,預設情況下使用的是直接模式

1.關聯模式

統計與目前資源關聯的另一個資源,觸發門檻值時,對目前資源限流

使用場景:比如使用者支付訂單的同時,要查詢訂單。查詢和修改操作會争搶資料庫資源,産生競争。是以當支付訂單狀态出發門檻值時,需要對查詢業務限流

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

以上示例,當

/user/{id}

QPS

達到門檻值時,将會對

/order/{id}

進行限流,避免影響到

/user/{id}

滿足一下條件時可以考慮使用關聯模式:

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

2.鍊路模式

統計從指定鍊路通路到本資源的請求,觸發門檻值時,對

指定鍊路

限流

例如有兩條請求鍊路,

  • /test1

    ->

    /common

  • /test2

    ->

    /common

如果隻希望統計從

/test1

進入

/common

的請求,則可以這樣配置:

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

這樣一來,當從

/test1

進入

/common

QPS

達到門檻值時,将對

/test1

進行限流

Sentinel

預設值标記

Controller

中的方法為資源,如果要标記其他方法,需要利用

@SentinelResource

注解

@SentinelResource("data")
public void queryData(){
    System.out.println("查詢資料");
}
           

Sentinel

預設會将

Controller

方法做

context

整合,導緻鍊路模式的流控失效,需要修改項目配置檔案,添加配置。

spring:
  cloud:
    sentinel:
      # 關閉context整合
      web-context-unify: false
      transport:
        dashboard: localhost:8080
           

3.流控效果

流控效果是指請求達到流控門檻值時應采取的措施

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

Sentinel

提供了三種流控效果:

  • 快速失敗:達到門檻值後,新的請求會被直接拒絕并抛出

    FlowException

    異常,這是預設的處理方式
  • Warm Up:預熱模式,對于超出門檻值的請求同樣是直接拒絕。但是這種門檻值會動态變化,從一個較小門檻值逐漸增加到最大門檻值
  • 排隊等待:讓所有請求按照先後順序排隊執行,兩個請求的間隔不能小于指定時長

1.Warm Up

warm up

也叫預熱模式,是應對服務冷啟動的一種方案。請求門檻值初始值為

threshold / coldFactor

,持續指定時長後,逐漸提高到

threshold

值,而

coldFactor

的預設值為3。

預熱模式主要是防止冷啟動時過高并發進而導緻故障

2.排隊等待

當請求超過

QPS

值時,快速失敗和預熱模式都會抛出異常。而排隊等待則是讓所有請求進入一個隊列中,然後按照門檻值允許的時間間隔依次執行。後來的請求必須等待前面的請求執行完成,如果請求預期的等待時間超出最大時長,則請求會被拒絕。

4.熱點參數限流

之前的限流是統計通路某個資源的所有請求,判斷是否超過

QPS

門檻值,而熱點參數限流是分别統計

參數值相同

的請求,判斷是否超過

QPS

門檻值。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

例如以下配置:

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

熱點資源 hot 的參數值為1的限流門檻值為4

熱點資源 hot 的參數值為2的限流門檻值為2

由此可見,熱點參數限流是一種更為細膩的限流方式,将限流規則降低到了參數級别上。

三 隔離和降級

限流是對微服務故障的一種預防措施,但是一旦服務已經出現了故障,則我們需要其他手段來防止出現級聯失敗甚至雪崩的問題。

而要将這些故障控制在一定範圍内,避免雪崩,就需要依賴于

線程隔離(艙壁模式)

熔斷降級

手段了。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

不管是

線程隔離

還是

熔斷降級

,都是對用戶端(調用者 / 消費者)的保護

1.FeignClient整合Sentinel

SpringCloud

中,微服務調用是通過Feign來實作的,是以做用戶端保護必須整合

Feign

Sentinel

步驟1:修改消費者微服務的項目配置檔案,開啟Feign的Sentinel功能

feign:
  sentinel:
    # 開啟 Feign 的 Sentinel 功能
    enabled: true
           

開啟以後,

Feign請求

将會作為一個資源存在于

Sentinel

中,這樣我們就可以對它進行

流控

等操作了

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

步驟2:給

FeignClient

編寫失敗後的降級邏輯

  • 方式1:FallbackClass,無法對遠端調用的異常做處理
  • 方式2:FallbackFactory,可以對遠端調用的異常做處理,一般使用這種

步驟3:編寫具體的降級邏輯

feign-api

項目中編寫 UserClientFallbackFactory

@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<IUserClientFeign> {
    @Override
    public IUserClientFeign create(Throwable throwable) {
        return new IUserClientFeign() {
            @Override
            public User findById(Integer id) {
                log.error("查詢異常");
                //未查詢到使用者時指定傳回内容,此處傳回空對象
                return new User();
            }
        };
    }
}
           

将其定義為

bean

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

@FeignClient

上指定

fallbackFactory

@FeignClient(value = "alibaba-user",fallbackFactory = UserClientFallbackFactory.class)
@RequestMapping("/user")
public interface IUserClientFeign {
    /**
     * 定義請求接口
     * @param id userId
     * @return 傳回對象
     */
    @GetMapping("/{id}")
    User findById(@PathVariable("id") Integer id);
}
           

2.線程隔離

線程隔離有兩種實作方式:

  • 線程池隔離
  • 信号量隔離(sentinel預設)
SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

線程池隔離原理:程式啟動時預設建立指定數量的線程池用于被其他微服務使用。當線程池被全部占據時将不再接受通路。業務處理完時線程解除占用。

信号量隔離原理:采用信号量計數器的辦法,當計數器達到指定的數量則不再接受通路。即有新業務時信号量+1,業務處理完成時信号量-1。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

在添加限流規則時,可以選擇兩種門檻值類型:

  • QPS:每秒請求數
  • 線程數:是該資源使用的

    tomcat

    線程數的最大值,也就是通過限制線程數量,實作艙壁模式
SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

3.熔斷降級

熔斷降級是解決雪崩問題的重要手段,其思路是由斷路器統計服務調用的異常比例,慢請求比例。如果超出門檻值則會熔斷該服務。即攔截該服務的一切請求;而當服務恢複時,斷路器也會放行該服務的請求。

以下是斷路器的工作流程

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

斷路器熔斷政策有三種:慢調用,異常比例,異常數。

1.慢調用

慢調用:業務的響應時間(RT)大于指定時長的請求認定為慢調用請求。在指定時間内請求數量超過設定的最小數量,慢調用比例大于設定的門檻值時觸發熔斷。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

以上示例的含義:對于

/order/{id}

資源,響應時間大于

2000ms

時認為時慢調用。統計

1000ms

内的請求,如果請求量超過

5次

且慢調用比例超過

0.5

,則觸發熔斷,熔斷時長為

5s

,然後進入

Half-Open

狀态,放行一次請求做測試。

2.異常比例或異常數

異常比例或異常數:統計指定時間内的調用,如果調用次數超過指定請求數,并且

異常的比例

異常數

達到設定的門檻值,則觸發熔斷。

異常比例或異常數的熔斷規則與調用時長無關,隻關心是否抛出異常和抛出異常的比例
SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

以上示例的含義:對于

/user/{id}

資源,統計

1000ms

内的請求,如果請求量超過

5次

且異常比例超過

0.5

,則觸發熔斷,熔斷時長為

5s

,然後進入

Half-Open

狀态,放行一次請求做測試。

四 授權規則

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

  • 白名單:來源(origin)在白名單内的調用者允許通路
  • 黑名單:來源(origin)在黑名單内的調用者不允許通路
SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

1.請求來源

Sentinel是通過RequestOriginParse這個接口的parseOrigin來擷取請求來源的

2.授權實作

在微服務中實作

RequestOriginParser

接口,這裡與統一網關約定使用

origin

作為請求頭參數名。

@Component
public class HeaderOriginParse implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        // 1.擷取請求頭
        String origin=httpServletRequest.getHeader("origin");
        if(StringUtils.isEmpty(origin)){
            origin="blank";
        }
        return origin;
    }
}
           

在統一網關服務的配置檔案中添加

origin

請求頭參數,值為

alibaba-gateway

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,alibaba-gateway
           

通過以上設定,可以保證隻要是來源于

統一網關服務

的請求,請求頭參數

origin

的值均為

alibaba-gateway

最後,在授權規則中填入來源名稱,設定白名單即可

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

添加了授權規則之後,對于

/order/{id}

的通路就必須通過統一網管來通路。

注意:請求來源參數(origin)的參數名和參數值切勿對外暴露,防止别人僞造請求頭

3.自定義異常攔截請求

預設情況下,發生限流,降級,熔斷時,都會抛出異常到調用方。如果要實作自定義異常時的傳回結果,需要實作

BlockExceptionHandler

接口。

BlockException

包含很多子類,分别對應不同的場景

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

以下是微服務調用時被

Sentinel

攔截的簡單示例:

@Component
public class SentinelBlockHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        String msg = "未知異常";
        if (e instanceof FlowException) {
            msg = "請求被限流";
        } else if (e instanceof DegradeException) {
            msg = "請求被降級";
        } else if (e instanceof ParamFlowException) {
            msg = "熱點參數被限流";
        }else if (e instanceof AuthorityException) {
            msg = "授權規則異常";
        }

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

以下是傳回示例:

傳回的Json格式自定義,此處隻做示範,不深究

{
    "status": 429,
    "msg": "授權規則異常"
}
           

五 規則持久化

在配置了

Sentinel

的各種規則後,重新開機服務會導緻規則丢失。

這是因為

Sentinel

将規則儲存在記憶體中,重新開機微服務規則自然會丢失。

1.規則管理模式

Sentinel

的控制台規則管理有三種模式:

  • 原始模式:

    Sentinel

    的預設模式,将規則儲存在記憶體,重新開機服務則丢失
  • pull模式:儲存在本地或資料庫,定時去讀取
  • push模式:儲存在

    Nacos

    ,監聽變更實時更新

1.Pull模式

Pull模式:控制台将配置的規則推送至Sentinel用戶端,而用戶端會将配置規則儲存在本地檔案或資料庫中。以後會定時去本地檔案或資料庫中查詢,更新本地規則。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

缺點:存在時效性問題,容易導緻資料不一緻

2.Push模式

Push模式:控制台将配置規則推送到遠端配置中心,例如

Nacos

Sentinel

用戶端監聽

Nacos

,擷取配置變更的推送消息,完成本地配置更新。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

2.實作push模式

1.引入依賴

在微服務中引入

sentinel-datasource-nacos

,監聽

Nacos

的依賴。

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

2.配置Nacos位址

在微服務的配置資訊中配置

Nacos

位址以及監聽的配置資訊

spring:
  cloud:
    sentinel:
      datasource:
        #流控
        flow:
          nacos:
            server-addr: localhost:8848
            data-id: orderserver-flow-rules #最終的配置檔案名稱
            group-id: SENTINEL_GROUP # 組ID
            rule-type: flow # 還可以是其他,具體請檢視 RuleType 的執行個體,需要配置多個時複制多份即可
        #降級
        degrade:
          nacos:
            server-addr: localhost:8848
            data-id: orderserver-degrade-rules #最終的配置檔案名稱
            group-id: SENTINEL_GROUP # 組ID
            rule-type: degrade
           

3.修改sentinel源碼

Sentinel-Dashboard

預設不支援nacos持久化,需要修改源碼

Github

上下載下傳

sentinel

的源碼,使用 IDE工具打開

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

1.在sentinel-dashboard源碼的pom檔案中,nacos的依賴預設的scope是test,隻能在測試時使用,這裡要去除:

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

2.在sentinel-dashboard的test包下,已經編寫了對nacos的支援,我們需要将其拷貝到main下。

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

3.修改

nacos

的位址,修改測試代碼中的

NacosConfig

類,讓其讀取application.properties中的值

@Configuration
@ConfigurationProperties(prefix = "nacos")
public class NacosConfig {

    /**
     * nacos位址
     */
    private String addr;
    
    @Bean
    public ConfigService nacosConfigService() throws Exception {
        return ConfigFactory.createConfigService(addr);
    }
    
    public void setAddr(String addr){
        this.addr=addr;
    }
    public String getAddr(){
        return this.addr;
    }

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }
}
           

4.在

sentinel-dashboard

application.properties

中添加nacos位址配置:

nacos.addr=localhost:8848
           

5.需要修改

com.alibaba.csp.sentinel.dashboard.controller.v2

包下的

FlowControllerV2

類,讓添加的Nacos資料源生效

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
           

6.修改前端頁面,添加一個支援nacos的菜單,

修改

src/main/webapp/resources/app/scripts/directives/sidebar/

目錄下的

sidebar.html

檔案,放開以下代碼:

<li ui-sref-active="active" ng-if="entry.appType==0">
  <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控規則 V1</a>
</li>
           

7.去掉測試單元,重新打包項目,然後運作

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)
java -jar sentinel-dashboard-1.8.6.jar
           

3.添加規則

在 開放出來的子產品中添加流控規則,将會被同步到 Nacos,進而實作持久化

SpringCloud(11)— 微服務保護(Sentinel)SpringCloud(11)— 微服務保護(Sentinel)

更多内容,參考Setinel官網

繼續閱讀