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

Gateway整合Sentinel

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

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

路由维度

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

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

    xml 复制代码
    <!--sentinel为gateway提供的适配包-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    </dependency>
  2. 增加配置类SentinelRouteConfiguration,实例化SentinelGatewayFilterSentinelBlockExceptionHandler对象,初始化限流规则

    java 复制代码
    @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();
        }
    
    }

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

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

    yml 复制代码
    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/**   # 断言,匹配规则
  4. 启动服务开始调试

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

自定义API维度

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

实现

  1. 导入Gateway的适配包

    xml 复制代码
    <!--sentinel为gateway提供的适配包-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    </dependency>
  2. 依然是实例化SentinelGatewayFilterSentinelBlockExceptionHandler对象,初始化限流规则,定义API分组

    java 复制代码
    @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();
        }
    
    }

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

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

    java 复制代码
    @RequestMapping("/user/group/findById")
    public String findGById(@RequestParam("id") Integer id) {
        return userInfo.getOrDefault(id, null);
    }
  4. 启动调用测试

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

超时配置

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

yml 复制代码
gateway:
  httpclient:
    connect-timeout: 5000 # 建立连接时间限制,单位毫秒
    response-timeout: 4s  # 响应时间的时间限制

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

shell 复制代码
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"}

被告知504,超时了~

CORS配置

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

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

yml 复制代码
cloud:
  nacos:
    discovery:
      server-addr: localhost:8848 # nacos地址
  gateway:
    httpclient:
      connect-timeout: 5000 # 建立连接时间限制,单位毫秒
      response-timeout: 4s  # 响应时间的时间限制
    globalcors:
      cors-configurations: 
        '[/**]':
          allowedOrigins: "*" # 允许的来源
          allowedMethods: "*" # 允许的方法
          allowedHeaders: "*" # 允许的请求头  *表示所有

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

相关推荐
小筱在线5 分钟前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
luoluoal10 分钟前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea15 分钟前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
小游鱼KF18 分钟前
Spring学习前置知识
java·学习·spring
扎克begod22 分钟前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
青灯文案123 分钟前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
我就是程序猿33 分钟前
tomcat的配置
java·tomcat
阳光阿盖尔39 分钟前
EasyExcel的基本使用——Java导入Excel数据
java·开发语言·excel
二十雨辰41 分钟前
[苍穹外卖]-12Apache POI入门与实战
java·spring boot·mybatis
程序员皮皮林41 分钟前
开源PDF工具 Apache PDFBox 认识及使用(知识点+案例)
java·pdf·开源·apache