天天看點

Spring Cloud :Gateway 內建 Sentinel (六)

目錄

一、概述

二、Sentinel快速搭建

1. sentinel 下載下傳

2. 指令啟動

3. sentinel dashborad

三、Gateway 整合 Sentinel

1. 引入 pom 依賴

 2. RulesController

3. bootstrap.yml 配置 sentinel服務位址

4. GatewayConfiguration 配置

四、啟動服務

1. 實時監控資料 

2. 簇點鍊路

五、總結

Spring Cloud Gateway 學習專欄

1. Spring Cloud : Gateway 服務網關認識(一)

2. Spring Cloud :整合Gateway 學習 (二)

3. Spring Cloud:Gateway 路由定義定位器 RouteDefinitionLocator (三)

4. Spring Cloud : Gateway 網關過濾器 GatewayFilter(四)

5. Spring Cloud : Gateway 網關限流(五)

6. Spring Cloud:Gateway 內建 Sentinel (六)

7. Spring Cloud : Gateway Redis動态路由 (七)

8. Spring Cloud : Gateway 整合Swagger (八)

如果發現本文有錯誤的地方,請大家毫不吝啬,多多指教,歡迎大家評論,謝謝!

一、概述

Spring Cloud Gateway是Spring Cloud官方推出的第二代網關架構,取代Zuul網關。網關作為流量的,在微服務系統中有着非常作用,網關常見的功能有路由轉發、權限校驗、限流控制等作用。Sentinel是阿裡開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個次元來保障服務之間的穩定性。(https://github.com/alibaba/Sentinel)

整體結構圖如下,将原有的 Spring Cloud Gateway中內建Hystrix替換成Sentinel來進行限流、降級等功能, Hystrix和Sentinel的差別可以參考:[Hystrix和Sentinel對比][1];

總結來說:Hystrix常用的線程池隔離會造成線程上下切換的overhead比較大;Hystrix使用的信号量隔離對某個資源調用的并發數進行控制,效果不錯,但是無法對慢調用進行自動降級;Sentinel通過并發線程數的流量控制提供信号量隔離的功能

Spring Cloud :Gateway 內建 Sentinel (六)

二、Sentinel快速搭建

1. sentinel 下載下傳

sentinel release下載下傳位址

https://github.com/alibaba/Sentinel/releases

Spring Cloud :Gateway 內建 Sentinel (六)

2. 指令啟動

采用java -jar 指令啟動 sentinel dashboard

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
           

 下面一段指令是把sentinel dashboard 服務也注冊到sentinel上去

-Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard 
           

登入參數配置:

從 Sentinel 1.6.0 起,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 分鐘;

3. sentinel dashborad

輸入賬号 :sentinel 密碼:sentinel

Spring Cloud :Gateway 內建 Sentinel (六)

三、Gateway 整合 Sentinel

1. 引入 pom 依賴

<!-- sentinel提供的gataway擴充卡 -->
       <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        <!--sentinel依賴包-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
           

 2. RulesController

import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Set;

/**
 * sentinel 接口控制
 * @date: 2021/4/20 14:52
 */
@RestController
public class RulesController {

    /**
     * Api定義
     * @date: 2021/4/22 10:23
     * @return: java.util.Set<com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition>
     */
    @GetMapping("/api")
    @SentinelResource("api")
    public Set<ApiDefinition> apiRules() {
        return GatewayApiDefinitionManager.getApiDefinitions();
    }

    /** 
     * 擷取 Route 限流配置資訊
     * @date: 2021/4/22 10:22
     * @return: java.util.Set<com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule> 
     */
    @GetMapping("/gateway")
    @SentinelResource("gateway")
    public Set<GatewayFlowRule> apiGateway() {
        return GatewayRuleManager.getRules();
    }

    /**
     * 流規則
     * @date: 2021/4/22 10:24
     * @return: java.util.List<com.alibaba.csp.sentinel.slots.block.flow.FlowRule>
     */
    @GetMapping("/flow")
    @SentinelResource("flow")
    public List<FlowRule> apiFlow() {
        return FlowRuleManager.getRules();
    }

}
           

3. bootstrap.yml 配置 sentinel服務位址

spring:
    cloud:
    sentinel:
      transport:
        dashboard: localhost:8890
        port: 8890
      # 服務啟動直接建立心跳連接配接
      eager: true
           

4. GatewayConfiguration 配置

/**
 * 網關 Sentinel 配置資訊配合限流規則
 * @date: 2021/4/22 10:37
 * @author: Zou.LiPing
 */
@Slf4j
@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;
    }

    /**
     * 配置SentinelGatewayFilter
     * @return GlobalFilter
     */
    @Bean
    @Order(-5)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initGatewayRules();
        initCustomizedApis();
        initBlockExceptionHandler();
    }

    /**
     * 初始化自定義異常
     * @date: 2021/4/20 17:21
     */
    private void initBlockExceptionHandler() {

        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            String value = serverWebExchange.getRequest().getPath().pathWithinApplication().value();
            Result<String> failed = Result.failed(ResultCode.TOO_MANY_REQUESTS, value);
            log.error("initBlockExceptionHandler.resp failed={}",failed);
            return ServerResponse
                    .status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .body(BodyInserters.fromObject(failed));
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

    /**
     * 初始化自定義API
     * @date: 2021/4/20 16:46
     *
     */
    private void initCustomizedApis() {
        /*
        ApiDefinition:使用者自定義的 API 定義分組,可以看做是一些 URL 比對的組合。
        比如我們可以定義一個 API 叫 user-api ,請求 path 模式為 /foo/** 和 /baz/** 的都歸到 my_api 這個 API 分組下面。
        限流的時候可以針對這個自定義的 API 分組次元進行限流。
        */
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition userApi = new ApiDefinition("user-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/user/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition productApi = new ApiDefinition("product-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/product/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(userApi);
        definitions.add(productApi);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    /**
     * 配置限流規則
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("user")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(2) // 限流門檻值
                .setIntervalSec(1) // 統計時間視窗,機關是秒,預設是 1 秒
        );
        rules.add(new GatewayFlowRule("product")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(1000) // 限流門檻值
                .setIntervalSec(1) // 統計時間視窗,機關是秒,預設是 1 秒
        );
        // 手動加載網關規則
        GatewayRuleManager.loadRules(rules);
    }

}
           

5. 自定義異常

/**
     * 初始化自定義異常
     * @date: 2021/4/20 17:21
     */
    private void initBlockExceptionHandler() {

        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            String value = serverWebExchange.getRequest().getPath().pathWithinApplication().value();
            Result<String> failed = Result.failed(ResultCode.TOO_MANY_REQUESTS, value);
            log.error("initBlockExceptionHandler.resp failed={}",failed);
            return ServerResponse
                    .status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .body(BodyInserters.fromObject(failed));
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
           

四、啟動服務

多次請求 curl http://localhost:9000/api

配置限流API分組規則如下

Spring Cloud :Gateway 內建 Sentinel (六)

1. 實時監控資料 

Spring Cloud :Gateway 內建 Sentinel (六)

2. 簇點鍊路

在右側可以設定流控、降級、熱點、授權操作

Spring Cloud :Gateway 內建 Sentinel (六)

流控設定

我們設定單機門檻值為 1 測試,每秒之内隻允許 1 個通路請求

Spring Cloud :Gateway 內建 Sentinel (六)

測試結果

再次通路 http://localhost:9000/api 接口

{"code":429,"message":"Too Many Requests","data":"/api"}
           

對應的參數屬性

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

五、總結

以上就是對Spring Cloud gateway 與 Sentinel 的整合方案,下一篇會介紹如何持久化我們配置資訊。

源碼位址

mall-gateway 這個項目

https://gitee.com/gaibianzlp/zlp-mall-demo.git

參考連結

1. Spring Cloud gateway 五 Sentinel整合

https://blog.csdn.net/autfish/article/details/90405679

https://blog.csdn.net/u014050029/article/details/106497762/

https://blog.csdn.net/kelly921011/article/details/105675055