• SpringCloudAlibaba Gateway(三)-整合Sentinel功能路由维度、API维度进行流控


    Gateway整合Sentinel

    ​ 前面使用过Sentinel组件对服务提供者、服务消费者进行流控、限流等操作。除此之外,Sentinel还支持对Gateway、Zuul等主流网关进行限流。

    ​ 自sentinel1.6.0版开始,Sentinel提供了Gateway的适配模块,能针对路由(route)和自定义API分组两个维度进行限流。

    路由维度

    路由维度是指配置文件中的路由条目,资源名是对应的routeId,相比自定义API维度,这是比较粗粒度的。看下如何实现:

    1. 导入Sentinel组件为Gateway提供的适配依赖包,在pom.xml中导入依赖

      
      <dependency>
          <groupId>com.alibaba.cspgroupId>
          <artifactId>sentinel-spring-cloud-gateway-adapterartifactId>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. 增加配置类SentinelRouteConfiguration实例化SentinelGatewayFilterSentinelBlockExceptionHandler对象,初始化限流规则

      @Configuration  // 配置类
      public class SentinelRouteConfiguration {   // 路由限流规则配置类
      
          private final List<ViewResolver> viewResolvers;
      
          private final ServerCodecConfigurer serverCodecConfigurer;
      
      
          public SentinelRouteConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
              this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
              this.serverCodecConfigurer = serverCodecConfigurer;
          }
      
          @PostConstruct
          public void initGatewayRules() {    // 初始化限流规则
              Set<GatewayFlowRule> rules = new HashSet<>();
              GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("user_route");    // 资源名(gateway中的路由id)
              gatewayFlowRule.setCount(1);    // 限流阈值
              gatewayFlowRule.setIntervalSec(1);  // 统计时间窗口,默认1s
      
              rules.add(gatewayFlowRule);
              GatewayRuleManager.loadRules(rules);    // 载入规则
          }
      
          @PostConstruct
          public void initBlockHandlers() {    // 限流后的响应
              BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {
                  Map<String, Object> result = new HashMap<>();
                  result.put("code", "0");
                  result.put("message", "您已被限流");
                  return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8).body(BodyInserters.fromObject(result));
              };
      
              GatewayCallbackManager.setBlockHandler(blockRequestHandler);    // 设置限流响应
          }
      
          @Bean
          @Order(Ordered.HIGHEST_PRECEDENCE)
          public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
              return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
          }
      
          @Bean
          @Order(Ordered.HIGHEST_PRECEDENCE)
          public GlobalFilter sentinelGatewayFilter() {   // 初始化限流过滤器
              return new SentinelGatewayFilter();
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49

      注意:Gateway限流是通过Filter实现的,主要是注入SentinelGatewayFilter实例和SentinelGatewayBlockExceptionHandler实例

    3. 在yml中,设置两个route,user_routeshop_route,上面主要是对user_route限流了,着重看下

      server:
        port: 8083
      
      spring:
        application:
          name: gateway   # 服务名
      
        cloud:
          nacos:
            discovery:
              server-addr: localhost:8848 # nacos地址
          gateway:
            routes: # 路由,可配置多个
            - id: user_route  # 路由id,唯一即可,默认UUID
              uri: lb://user  # 路由地址(匹配成功后的服务地址) user是用户服务的服务名称
              order: 1  # 路由优先级,默认0,越低优先级越高
              predicates:
              - Path=/user/**   # 断言,匹配规则
      
            - id: shop_route  # 路由id,唯一即可,默认UUID
              uri: lb://shop  # 路由地址(匹配成功后的服务地址) shop是用户服务的服务名称
              order: 1  # 路由优先级,默认0,越低优先级越高
              predicates:
              - Path=/shop/**   # 断言,匹配规则
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
    4. 启动服务开始调试

      在这里插入图片描述

      成功完成路由级别的限流,那么后面看看API维度的限流

    自定义API维度

    ​ 上面那种限流方式可以看出灵活性不够高。自定义的API维度可以利用Sentinel提供的API自定义分组来进行限流。相比路由维度,这是一种更加细粒度的限流方式。

    实现

    1. 导入Gateway的适配包

      
      <dependency>
          <groupId>com.alibaba.cspgroupId>
          <artifactId>sentinel-spring-cloud-gateway-adapterartifactId>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. 依然是实例化SentinelGatewayFilterSentinelBlockExceptionHandler对象,初始化限流规则,定义API分组

      @Configuration  // 配置类
      public class SentinelRouteConfiguration {   // 路由限流规则配置类
      
          private final List<ViewResolver> viewResolvers;
      
          private final ServerCodecConfigurer serverCodecConfigurer;
      
      
          public SentinelRouteConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
              this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
              this.serverCodecConfigurer = serverCodecConfigurer;
          }
      
          @PostConstruct
          public void initGatewayRules() {    // 初始化限流规则
              Set<GatewayFlowRule> rules = new HashSet<>();
              GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("user_api");    // 资源名,api分组的名称(自定义)
              gatewayFlowRule.setCount(1);    // 限流阈值
              gatewayFlowRule.setIntervalSec(1);  // 统计时间窗口,默认1s
      
              rules.add(gatewayFlowRule);
              GatewayRuleManager.loadRules(rules);    // 载入规则
          }
      
          @PostConstruct
          public void initCustomizedApis() {
              Set<ApiDefinition> apiDefinitions = new HashSet<>();
              ApiDefinition apiDefinition = new ApiDefinition("user_api") // 定义 api分组
                      .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                              add(new ApiPathPredicateItem()
                                      .setPattern("/user/group/**")	// 路径匹配规则
                                      .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); // 匹配策略,前缀匹配
                          }});
              apiDefinitions.add(apiDefinition);
      
              GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions); // 载入API分组定义
      
          }
      
          @PostConstruct
          public void initBlockHandlers() {    // 限流后的响应
              BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {
                  Map<String, Object> result = new HashMap<>();
                  result.put("code", "0");
                  result.put("message", "您已被限流");
                  return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8).body(BodyInserters.fromObject(result));
              };
      
              GatewayCallbackManager.setBlockHandler(blockRequestHandler);    // 设置限流响应
          }
      
          @Bean
          @Order(Ordered.HIGHEST_PRECEDENCE)
          public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
              return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
          }
      
          @Bean
          @Order(Ordered.HIGHEST_PRECEDENCE)
          public GlobalFilter sentinelGatewayFilter() {   // 初始化限流过滤器
              return new SentinelGatewayFilter();
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64

      唯一要注意的是:路由匹配规则如果是单一的一个具体接口,不是匹配符,那么后面的匹配策略就没有必要再去配置了(setMatchStrategy()方法)

    3. 定义一个/user/group/findById接口

      @RequestMapping("/user/group/findById")
      public String findGById(@RequestParam("id") Integer id) {
          return userInfo.getOrDefault(id, null);
      }
      
      • 1
      • 2
      • 3
      • 4
    4. 启动调用测试

      在这里插入图片描述

      可以看到配置的没有问题,满足/user/group/**规则的请求,有被限流到

    超时配置

    Gateway默认没有超时的限制,也就是数据什么时候返回,就等待到什么时候。如果不想等待,可以增加对超时的配置

    gateway:
      httpclient:
        connect-timeout: 5000 # 建立连接时间限制,单位毫秒
        response-timeout: 4s  # 响应时间的时间限制
    
    • 1
    • 2
    • 3
    • 4

    尝试下,接口睡眠5s,再次调用

    curl localhost:8083/user/group/findById?id=1
    {"timestamp":"2023-09-02T00:59:47.184+00:00","path":"/user/group/findById","status":504,"error":"Gateway Timeout","requestId":"7f5ff558-1"}
    
    • 1
    • 2

    被告知504,超时了~

    CORS配置

    涉及到前后端分离的跨域请求时,浏览器访问后端地址通常提示No Access-Control-Allow-Origin header is present on the requested resource

    可以在gateway中增加跨域配置,或者前端去配置都可以,自行协商。

    cloud:
      nacos:
        discovery:
          server-addr: localhost:8848 # nacos地址
      gateway:
        httpclient:
          connect-timeout: 5000 # 建立连接时间限制,单位毫秒
          response-timeout: 4s  # 响应时间的时间限制
        globalcors:
          cors-configurations: 
            '[/**]':
              allowedOrigins: "*" # 允许的来源
              allowedMethods: "*" # 允许的方法
              allowedHeaders: "*" # 允许的请求头  *表示所有
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    通常情况下,也可以不是按照全部允许来做,按照你的项目实际开发需求搞就行了。

  • 相关阅读:
    Vision China 2023(深圳)倒计时,51camera诚邀您莅临观展
    WPF引入 ttf 图标文件使用记录
    【软件测试】 1+X初级 功能测试试题
    SpringCloud03 --- 搭建Nacos集群、Feign远程调用、Gateway服务网关
    JMeter实现接口自动化测试
    数据库(1):数据库初识与基本操作
    第6章:MATLAB文本数据处理进阶篇的目录 (MATLAB入门课程)
    HTML的特殊字符
    『亚马逊云科技产品测评』活动征文|Amazon RDS创建并连接到 MySQL 数据库实例
    副高级职称的要求真的很难达到吗?
  • 原文地址:https://blog.csdn.net/weixin_45248492/article/details/132634792