SpringCloudAlibaba 六、Sentinel 服務保護( 服務降級/ 熔斷/ 數據持久化 /gateway 整合 Sentinel )

一、Sentinel 描敘

1、Sentinel作用

Sentinel,中文翻譯爲哨兵,是爲微服務提供流量控制、熔斷降級的功能,它和Hystrix提供的功能一樣,可以有效的解決微服務調用產生的“雪崩”效應,爲微服務系統提供了穩定性的解決方案。隨着Hytrxi進入了維護期,不再提供新功能,Sentinel是一個不錯的替代方案。通常情況,Hystrix採用線程池對服務的調用進行隔離,Sentinel才用了用戶線程對接口進行隔離,二者相比,Hystrxi是服務級別的隔離,Sentinel提供了接口級別的隔離,Sentinel隔離級別更加精細,另外Sentinel直接使用用戶線程進行限制,相比Hystrix的線程池隔離,減少了線程切換的開銷。另外Sentinel的DashBoard提供了在線更改限流規則的配置,也更加的優化。

2、Sentinel 具有以下特徵

從官方文檔的介紹,Sentinel 具有以下特徵:

豐富的應用場景: Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峯填谷、實時熔斷下游不可用應用等。
完備的實時監控: Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級數據,甚至 500 臺以下規模的集羣的彙總運行情況。
廣泛的開源生態: Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
完善的 SPI 擴展點: Sentinel 提供簡單易用、完善的 SPI 擴展點。您可以通過實現擴展點,快速的定製邏輯。例如定製規則管理、適配數據源等。

3、如何在Spring Cloud中使用Sentinel

Sentinel的使用分爲兩部分:

sentinel-dashboard:與hystrix-dashboard類似,但是它更爲強大一些。除了與hystrix-dashboard一樣提供實時監控之外,還提供了流控規則、熔斷規則的在線維護等功能。

客戶端整合:每個微服務客戶端都需要整合sentinel的客戶端封裝與配置,才能將監控信息上報給dashboard展示以及實時的更改限流或熔斷規則等。

4、Sentinel 和 hytrix的區別

在這裏插入圖片描述

二、Sentinel 控制檯 Sentinel DashBoard

Sentinel DashBoard
Sentinel 控制檯提供一個輕量級的控制檯,它提供機器發現、單機資源實時監控、集羣資源彙總,以及規則管理的功能.
Sentinel DashBoard下載地址:https://github.com/alibaba/Sentinel/releases
下載 jar 文件即可
在這裏插入圖片描述

下載完成後,以以下的命令啓動

java -jar sentinel-dashboard-1.6.1.jar

訪問 localhost:8080
默認啓動端口爲8080,啓動時可以添加 -Dserver.port=8081 的形式改變默認端口
默認賬號密碼:sentinel / sentinel

登錄成功後展示如下配置成功
在這裏插入圖片描述
其他啓動指定:
-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 分鐘;

三、springboot 整合 Sentinel

Sentinel 有兩種配置方式
1、使用代碼+註解配置(看7)
2、使用控制檯配置(看6)

3.1、pom.xml 依賴

        <!-- sentinel -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>

3.2、application.yml

  • 在工程的配置文件application.yml文件中配置,需要新增2個配置:
  • spring.cloud.sentinel.transport.port: 8719 ,這個端口配置會在應用對應的機器上啓動一個 Http Server,該 Server 會與 Sentinel 控制檯做交互。比如 Sentinel 控制檯添加了1個限流規則,會把規則數據 push 給這個 Http Server 接收,Http Server 再將規則註冊到 Sentinel 中。
  • spring.cloud.sentinel.transport.dashboard: 8080,這個是Sentinel DashBoard的地址。
##  在工程的配置文件application.yml文件中配置,需要新增2個配置:
##  spring.cloud.sentinel.transport.port: 8719 ,這個端口配置會在應用對應的機器上啓動一個 Http Server,該 Server 會與 Sentinel 控制檯做交互。比如 Sentinel 控制檯添加了1個限流規則,會把規則數據 push 給這個 Http Server 接收,Http Server 再將規則註冊到 Sentinel 中。
##  spring.cloud.sentinel.transport.dashboard: 8080,這個是Sentinel DashBoard的地址。

server:
  port: 8090
spring:
  application:
    name: alibaba-alibaba-sentinel
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.177.128:8848
    ###  
    sentinel:
      transport:
        ### 當前項目Http Server ip,主要主要是和Sentinel  的DashBoard控制檯系統進行通訊
        port: 8719
        ###  Sentinel 控制檯DashBoard的地址
        dashboard: http://localhost:8080


3.3、註解 @SentinelResource參數說明:

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

注:1.6.0 之前的版本 fallback 函數只針對降級異常(DegradeException)進行處理,不能針對業務異常進行處理。

3.4、測試接口1(指定方法)

關於@SentinelResource註解最主要的兩個用法:限流控制和熔斷降級的具體使用案例介紹完了。另外,該註解還有一些其他更精細化的配置,比如忽略某些異常的配置、默認降級函數等等

package com.example.alibabasentinel.service;


import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


/**
 * TODO  服務熔斷/限流/降級(指定方法)
 * @author 王鬆
 * @mail  [email protected]
 * @date  2020/1/22 0022 20:35
 */
@RestController
public class TestService1Impl {

    /**
     * TODO   測試方法(當前類處理-服務降級)
     *  getName :限流名稱,控制檯添加一定要選擇該值,勿添加爲 /getName 默認名,否則exceptionHandler  參數無效
     *  exceptionHandler : 服務限流降級後執行的方法
     */
    @GetMapping("/getName")
    @SentinelResource(value = "getName", blockHandler = "exceptionHandler")
    public String getName() {
        return "wangsong";
    }

    /**
     * TODO   處理限流與阻塞(服務降級返回友好提示),方法名對應 blockHandler的參數,服務限流降級後執行的方法
     * 
     */
    public String exceptionHandler(BlockException ex) {
        System.out.println("====" + ex);
        return "當前訪問人數過多,請稍後再試";
    }
}

參考返回值:
1、當前訪問人數過多,請稍後再試
2、正在排隊中…

3.5、測試接口2(指定Handler類的方法)

添加測試方法

 package com.example.alibabasentinel.service;


import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.example.alibabasentinel.handler.MyBlockHandlerClass;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
  * TODO   服務熔斷/限流/降級(指定Handler類的方法)
  * @author 王鬆
  * @mail  [email protected]
  * @date  2020/1/22 0022 20:40 
  */
@RestController
public class TestService2Impl {

    /**
     * TODO    測試方法(指定Handler類處理-服務降級),blockHandlerClass = MyBlockHandlerClass.class
     *  getName :限流名稱,控制檯添加一定要選擇該值,勿添加爲 /getName2 默認名,否則exceptionHandler  參數無效
     *  blockHandlerClass :服務限流/異常等指定類執行降級方法
     *  blockHandler: 服務限流執行的降級方法
     *  fallback :  服務異常執行的降級方法
     */
    @GetMapping("/getName2")
    @SentinelResource(value = "getName2", blockHandlerClass = MyBlockHandlerClass.class, blockHandler = "blockHandlerFunc", fallback = "fallbackFunc")
    public String getName2(String a) {
        return "wangsong2";
    }
}

添加 MyBlockHandlerClass 類 處理服務熔斷降級

import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyBlockHandlerClass {

    // 處理限流與阻塞(服務降級返回友好提示)
    public static String blockHandlerFunc(String a, BlockException e){
        log.warn("限流了==="+a,e);
        return "當前訪問人數過多,請稍後再試";
    }


    // 服務異常(降級處理)
    public static String fallbackFunc(String a){
        log.warn("異常==="+a);
        return "";
    }
}

3.6、控制檯添加流控並測試

qbs:閾值 = 每秒能調用該接口多少次
線程數:閾值 = 請求最多有多少個線程池來處理(默認1)
項目啓動後等一下下,控制檯在會出現當前服務信息
先訪問接口 /getName 幾次,控制檯纔會出現該接口信息,

3.6.1、添加流控(接口到達閾值自動熔斷)

getName = @SentinelResource註解的value 值,如果流控添加到 /getName ,將無法使用服務降級功能
在這裏插入圖片描述

添加流控qbs,設置閾值爲2
qbs:閾值 = 每秒能調用該接口多少次
線程數:閾值 = 請求最多有多少個線程池來處理(默認1)
在這裏插入圖片描述

3.6.2、訪問接口(服務降級)

正常訪問接口返回響應數據,如當前:wangsong
到達閾值上限 ,返回:Blocked by Sentinel (flow limiting)

設置 blockHandle, 到達閾值上限,返回如下:
如:@SentinelResource(value=“getName”, blockHandler = “exceptionHandler”),

在這裏插入圖片描述

3.7、手動配置流控參考

步驟3.6我們是通過控制檯添加的流控,我們也可以使用java 代碼添加流控
可在啓動項目初始化容器時添加,避免重啓服務失效
定義限流的接口添加註解,具體看 3.4, 3.5
@SentinelResource(value = “getOrder”, blockHandler = “exceptionHandler”)


// 流控接口
private static final String GETORDER_KEY = "getOrder";

// 調用接口方式添加流控
@RequestMapping("/initFlowQpsRule")
public String initFlowQpsRule() {

    List<FlowRule> rules = new ArrayList<FlowRule>();
    FlowRule rule1 = new FlowRule();
    rule1.setResource(GETORDER_KEY);
    // 設置QPS控制在2以內
    rule1.setCount(1);
    // 限流規則QPS
    rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule1.setLimitApp("default");
    rules.add(rule1);
    // 添加流控集
    FlowRuleManager.loadRules(rules);
    return "....限流配置初始化成功..";
}

四、Sentinel 數據持久化

五、gateway 整合 Sentinel 實現服務限流

1、添加sentinel 限流核心配置

package com.gateway.sentinel;

import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import java.util.Collections;
import java.util.List;
/**
  * TODO  sentinel 限流核心配置
  * @author ws
  * @mail  [email protected]
  * @date  2020/2/17 0017 14:08
  */
@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }


}

2、添加sentinel 限流規則

package com.gateway.sentinel;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

/**
 * TODO  限流規則配置
 *
 * @author ws
 * @mail [email protected]
 * @date 2020/2/17 0017 14:09
 */
@Slf4j
@Component
public class SentinelApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        initGatewayRules();

    }

    /**
     * 配置限流規則
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("test")
                // 限流閾值
                .setCount(1)
                // 統計時間窗口,單位是秒,默認是 1 秒
                .setIntervalSec(1)
        );
        GatewayRuleManager.loadRules(rules);
    }
}

3、配置自定義返回信息(如果配置了全局異常看四)

package com.gateway.sentinel;

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;
/**
  * TODO  限流錯誤返回信息
  * @author ws
  * @mail  [email protected]
  * @date  2020/2/17 0017 14:13
  */
public class JsonSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
    public JsonSentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] datas = "{\"code\":403,\"msg\":\"API接口被限流\"}".getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
        return serverHttpResponse.writeWith(Mono.just(buffer));
    }

}




4、全局異常返回自定義信息

全局異常配置暫不做說明
在這裏插入圖片描述

六、Sentinel 對 Feign 的支持

後續在補
Sentinel 對 Feign 的支持:https://segmentfault.com/a/1190000019183725
Sentinel 流控: https://blog.csdn.net/u013792404/article/details/101287064
Sentinel 流控:https://blog.csdn.net/meteor_93/article/details/96470653

本文參考文章1:https://blog.csdn.net/u013184307/article/details/95973022
本文參考文章2:https://blog.csdn.net/a772304419/article/details/99689562

發佈了198 篇原創文章 · 獲贊 30 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章