天天看点

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