说一说 SpringCloud Gateway 使用 Sentinel 实现服务限流熔断​

简介: 我是小假 期待与你的下一次相遇 ~

1、介绍

Alibaba Sentinel 支持对 Spring Cloud Gateway、Netflix Zuul 等主流的 API Gateway 进行限流与熔断配置。

本文将介绍如何在 Spring Cloud Gateway 中使用 Alibaba Sentinel 进行限流配置,从而代替 Hystrix.

2、集成步骤

2.1. 首先需在Gateway网关模块引入以下依赖配置

(以 Maven 为例):

方案一:只需引入 sentinel-spring-cloud-gateway-adapter 无需引入 spring-cloud-starter-alibaba-sentinel

  1. <dependency>
  2.    <groupId>com.alibaba.csp</groupId>
  3.    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
  4.    <version>x.y.z</version>
  5. </dependency>

方案二:如果需要在Sentinel 控制台管理 网关模块的限流,那么推荐这种配置方式

如果想引入 spring-cloud-starter-alibaba-sentinel 那么就把上面那个 adapter 替换成 spring-cloud-alibaba-sentinel-gateway

  1. <!-- 没添加该依赖的话不能从application配置文件配置sentinel -->
  2. <dependency>
  3.    <groupId>com.alibaba.cloud</groupId>
  4.    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  5. </dependency>
  6. <dependency>
  7.    <groupId>com.alibaba.cloud</groupId>
  8.    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
  9. </dependency>

2.2 然后在使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。

  1. @Configuration
  2. public class GatewayConfiguration {
  3.    private final List<ViewResolver> viewResolvers;
  4.    private final ServerCodecConfigurer serverCodecConfigurer;
  5.    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
  6.                                ServerCodecConfigurer serverCodecConfigurer) {
  7.        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
  8.        this.serverCodecConfigurer = serverCodecConfigurer;
  9.    }
  10.    @Bean
  11.    @Order(Ordered.HIGHEST_PRECEDENCE)
  12.    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
  13.        // Register the block exception handler for Spring Cloud Gateway.
  14.        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
  15.    }
  16.    @Bean
  17.    @Order(Ordered.HIGHEST_PRECEDENCE)
  18.    public GlobalFilter sentinelGatewayFilter() {
  19.        return new SentinelGatewayFilter();
  20.    }
  21. }

2.3 现在可以选择在控制台中配置Sentinel限流规则 或者选择直接在Java代码中配置限流规则

2.3.1. 如果选择使用 Sentinel 控制台配置限流-熔断规则,那么还需要在 gateway 的配置文件中做出如下配置:

  1. spring:
  2.  cloud:
  3.    # Sentinel 控制台连接配置
  4.    sentinel:
  5.      transport:
  6.       # 当前服务与控制台交互的端口号,默认为8719,同一个机器上若有多个应用于控制台交互时需要设置成不同的端口
  7.        port: 8739
  8.        dashboard: 10.1.3.77:9090
  9.      # 服务启动时直接建立心跳连接
  10.      eager: true
  11.      # Sentinel 储存规则的数据源配置(我这里使用的是Nacos来存储Sentinel的限流规则)
  12.      datasource:
  13.        ds:
  14.          nacos:
  15.            # Nacos 服务地址(可配置单点,或者集群的VIP地址)
  16.            server-addr: 10.1.3.76:8848
  17.            dataId: ${spring.application.name}-sentinel
  18.            groupId: DEFAULT_GROUP
  19.            namespace: sms-dev
  20.            rule-type: flow
  21. # Sentinel 控制台鉴权配置
  22. sentinel:
  23.  dashboard:
  24.    auth:
  25.      username: sentinel
  26.      password: sentinel

此时就可以在Sentinel控制台页面中配置限流规则,或者在 Nacos 配置中心中添加限流配置文件sms-gateway-sentinel

  1. [
  2.    {
  3.        "resource": "api-service",
  4.        "limitApp": "default",
  5.        "grade": 1,
  6.        "count": 2,
  7.        "strategy": 0,
  8.        "controlBehavior": 0,
  9.        "clusterMode": false
  10.    },
  11.    {
  12.        "resource": "url-proxy-1",
  13.        "limitApp": "default",
  14.        "grade": 1,
  15.        "count": 2,
  16.        "strategy": 0,
  17.        "controlBehavior": 0,
  18.        "clusterMode": false
  19.    }
  20. ]

2.3.2 如果选择在Java代码中配置限流规则

下面是一个详细的配置示例

  1. import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
  2. import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
  3. import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
  4. import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
  5. import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
  6. import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
  7. import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
  8. import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
  9. import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
  10. import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
  11. import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
  12. import com.alibaba.csp.sentinel.slots.block.RuleConstant;
  13. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
  14. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
  15. import org.springframework.beans.factory.ObjectProvider;
  16. import org.springframework.cloud.gateway.filter.GlobalFilter;
  17. import org.springframework.context.annotation.Bean;
  18. import org.springframework.context.annotation.Configuration;
  19. import org.springframework.core.Ordered;
  20. import org.springframework.core.annotation.Order;
  21. import org.springframework.http.HttpStatus;
  22. import org.springframework.http.MediaType;
  23. import org.springframework.http.codec.ServerCodecConfigurer;
  24. import org.springframework.web.reactive.function.BodyInserters;
  25. import org.springframework.web.reactive.function.server.ServerResponse;
  26. import org.springframework.web.reactive.result.view.ViewResolver;
  27. import org.springframework.web.server.ServerWebExchange;
  28. import reactor.core.publisher.Mono;
  29. import javax.annotation.PostConstruct;
  30. import java.util.*;
  31. /**
  32. * @description: 网关限流配置
  33. * @create: 2020-08-26 12:23
  34. **/
  35. @Configuration
  36. public class GatewaySentinelConfiguration {
  37.    private final List<ViewResolver> viewResolvers;
  38.    private final ServerCodecConfigurer serverCodecConfigurer;
  39.    public GatewaySentinelConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
  40.                                        ServerCodecConfigurer serverCodecConfigurer) {
  41.        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
  42.        this.serverCodecConfigurer = serverCodecConfigurer;
  43.    }
  44.    @Bean
  45.    @Order(Ordered.HIGHEST_PRECEDENCE)
  46.    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
  47.        // Register the block exception handler for Spring Cloud Gateway.
  48.        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
  49.    }
  50.    @Bean
  51.    @Order(Ordered.HIGHEST_PRECEDENCE)
  52.    public GlobalFilter sentinelGatewayFilter() {
  53.        return new SentinelGatewayFilter();
  54.    }
  55.    /**
  56.    * Spring 容器初始化的时候执行该方法
  57.    */
  58.    @PostConstruct
  59.    public void doInit() {
  60.        // 加载网关限流规则
  61.        initGatewayRules();
  62.        // 加载自定义限流异常处理器
  63.        initBlockHandler();
  64.    }
  65.    /**
  66.    * 网关限流规则
  67.    * 建议直接在 Sentinel 控制台上配置
  68.    */
  69.    private void initGatewayRules() {
  70.        Set<GatewayFlowRule> rules = new HashSet<>();
  71.        /*
  72.        resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
  73.        count:限流阈值
  74.        intervalSec:统计时间窗口,单位是秒,默认是 1 秒
  75.        */
  76.        // rules.add(new GatewayFlowRule("order-service")
  77.        //         .setCount(3) // 限流阈值
  78.        //         .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
  79.        // --------------------限流分组----------start----------
  80.        rules.add(new GatewayFlowRule("url-proxy-1")
  81.                  .setCount(1) // 限流阈值
  82.                  .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
  83.        rules.add(new GatewayFlowRule("api-service")
  84.                  .setCount(5) // 限流阈值
  85.                  .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
  86.        // --------------------限流分组-----------end-----------
  87.        // 加载网关限流规则
  88.        GatewayRuleManager.loadRules(rules);
  89.        // 加载限流分组
  90.        initCustomizedApis();
  91.        // ---------------熔断-降级配置-------------
  92.        DegradeRule degradeRule = new DegradeRule("api-service") // 资源名称
  93.            .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) // 异常比率模式(秒级)
  94.            .setCount(0.5) // 异常比率阈值(50%)
  95.            .setTimeWindow(10); // 熔断降级时间(10s)
  96.        // 加载规则.
  97.        DegradeRuleManager.loadRules(Collections.singletonList(degradeRule));
  98.    }
  99.    /**
  100.    * 自定义限流异常处理器
  101.    */
  102.    private void initBlockHandler() {
  103.        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
  104.            @Override
  105.            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
  106.                Map<String, String> result = new HashMap<>(3);
  107.                result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
  108.                result.put("message", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
  109.                result.put("x","xx");
  110.                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
  111.                    .contentType(MediaType.APPLICATION_JSON)
  112.                    //                        .body(BodyInserters.fromValue(result));
  113.                    .body(BodyInserters.fromObject(result));
  114.            }
  115.        };
  116.        // 加载自定义限流异常处理器
  117.        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
  118.    }
  119.    /**
  120.    * 分组限流
  121.    */
  122.    private void initCustomizedApis() {
  123.        //demo
  124.        Set<ApiDefinition> definitions = new HashSet<>();
  125.        // product-api 组
  126.        ApiDefinition api1 = new ApiDefinition("product-api")
  127.            .setPredicateItems(new HashSet<ApiPredicateItem>() {{
  128.                // 匹配 /product-service/product 以及其子路径的所有请求
  129.                add(new ApiPathPredicateItem().setPattern("/product-service/product/**")
  130.                    .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
  131.            }});
  132.        // order-api 组
  133.        ApiDefinition api2 = new ApiDefinition("order-api")
  134.            .setPredicateItems(new HashSet<ApiPredicateItem>() {{
  135.                // 只匹配 /order-service/order/index
  136.                add(new ApiPathPredicateItem().setPattern("/order-service/order/index"));
  137.            }});
  138.        definitions.add(api1);
  139.        definitions.add(api2);
  140.        // 加载限流分组
  141.        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
  142.    }
  143. }

Tips:这种配置方式只适合固定的限流规则配置,如果需要灵活变动,那么建议使用上面那种方式

3、总结

如果 Gateway 用 Sentinel

  • 建议在 Sentinel 控制台对 网关模块 进行具体的限流,熔断降级配置。
  • 否则还是推荐直接用 Gateway 内置的 RequestRateLimiterHystrix 进行熔断限流配置。

还可以参考:https://github.com/alibaba/Sentinel/wiki/Guideline:-从-Hystrix-迁移到-Sentinel

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度: 即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId* 在网关上用 Sentinel , 那么默认情况下一个 RouteId 对应的服务就是一个资源。
  • GatewayFlowRule: 网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。

自定义 API 维度: 用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如可以定义一个 API 叫 my_api,请求 path 模式为 /foo/**/baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

官方Demo 示例:https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-spring-cloud-gateway


相关文章
|
NoSQL 算法 Java
面试官:网关如何实现限流?
面试官:网关如何实现限流?
733 2
面试官:网关如何实现限流?
|
域名解析 Kubernetes Java
图文详述Nacos配置中心使用:应用间配置共享、扩展配置文件加载优先级、新老版本差异
图文详述Nacos配置中心使用:应用间配置共享、扩展配置文件加载优先级、新老版本差异
5827 1
图文详述Nacos配置中心使用:应用间配置共享、扩展配置文件加载优先级、新老版本差异
|
10月前
|
负载均衡 Java Nacos
SpringCloud基础2——Nacos配置、Feign、Gateway
nacos配置管理、Feign远程调用、Gateway服务网关
SpringCloud基础2——Nacos配置、Feign、Gateway
|
11月前
|
Kubernetes 数据库 容器
k8s快速部署xxl-job
k8s快速部署xxl-job
|
11月前
|
前端开发 JavaScript 测试技术
"React新手入门的神奇之处:如何用React构建第一个应用,让你的项目一鸣惊人?"
【8月更文挑战第31天】本文详细介绍了如何从头开始构建首个React应用。React作为当今Web开发中备受欢迎的前端框架,采用组件化设计实现界面构建,便于维护与扩展。文章首先解释了React的基础概念,接着演示了使用Create React App搭建项目的步骤,展示了基础组件编写方法及React Router的简单运用,并分享了一些实用的最佳实践建议,帮助读者快速上手React开发。
177 0
|
Java Sentinel 微服务
|
SpringCloudAlibaba Java Sentinel
十四.SpringCloudAlibaba极简入门-Sentinel对Gateway网关进行限流
服务网关在微服务架构中充当了请求访问入口的角色,是非常重要的一个部分,在高并发的系统中我们通常会在网关层通过流控降级等手段把多余的请求拒绝在外来防止微服务被高并发请求打垮,在之前我们有讨论过《服务网关Spring Cloud Gateway》和 《Sentinel流控》,一个是服务网关,一个是流控降级,本篇文章要讨论的是如何使用Sentinel对Gateway进行流控
|
Java Maven
关于idea依赖引用正确但是 编译报错“找不到符号”问题
解决烧脑问题,尝试重启、清理Maven重编译或改字符集删除target。一张图片总结其他方法,简单易行。重点检查Maven主路径,避免默认捆绑设置。附两张图片示例,完成操作后重新编译启动,问题解决
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
Sentinel
sentinel的@SentinelResource注解使用
sentinel的@SentinelResource注解使用
642 0