Spring Cloud Alibaba微服务实战十二 - 网关限流

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

导读:通过前面的章节我们在微服务层做了限流,并且集成了SpringCloud Gateway,本章主要内容是将限流功能从微服务迁移到网关层。

SpringCloud Gateway 原生限流

Springcloud Gateway 原生限流主要基于过滤器实现,我们可以直接使用内置的过滤器RequestRateLimiterGatewayFilterFactory,目前RequestRateLimiterGatewayFilterFactory的实现依赖于 Redis,所以我们还要引入spring-boot-starter-data-redis-reactive

POM依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifatId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>

限流配置

    spring:
      cloud:
        gateway:
          routes:
          - id: account-service
            uri: lb://account-service
            order: 10000
            predicates:
            - Path=/account-service/**
            filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 3
                key-resolver: "#{@ipKeyResolver}"

主要是配置三个主要参数:

  • redis-rate-limiter.replenishRate :
    允许用户每秒处理多少个请求
  • redis-rate-limiter.burstCapacity :
    令牌桶的容量,允许在一秒钟内完成的最大请求数
  • key-resolver :
    用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

配置Bean

    /**
    * 自定义限流标志的key,多个维度可以从这里入手
    * exchange对象中获取服务ID、请求信息,用户信息等
    */
    @Bean
    KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }

Sentinel 限流

我们之前的章节已经讲过Sentinel的使用方法,如果有不清楚的可以翻看之前的章节,这里主要说一下与SpringCloud gateway的整合。

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

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId自定义
  • API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

下面是我们的整合步骤

POM依赖

    <dependency>
    	<groupId>com.alibaba.cloud</groupId>
    	<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>com.alibaba.cloud</groupId>
    	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>com.alibaba.csp</groupId>
    	<artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>

由于需要使用 nacos作为sentinel的配置中心,所以也引入了sentinel-datasource-nacos

Bootstrap配置

    ...
    spring:
      cloud:
        sentinel:
          transport:
            dashboard: 10.0.10.48:8858
          eager: true
          datasource:
            ds:
              nacos:
                server-addr: 10.0.10.48:8848
                data-id: gateway-sentinel-flow
                group-id: DEFAULT_GROUP
                rule-type: gw-flow
    ...

这里主要是sentinel的相关配置,从nacos配置中心获取 gateway-sentinel-flow 配置文件,限流类型是网关类型gw-flow。

限流配置

在nacos配置管理public页面建立 data-id 为 gateway-sentinel-flow 的配置文件(json格式),给account-serviceproduct-service添加限流规则。

    [
      {
        "resource": "account-service",
        "count": 5,
        "grade": 1,
        "paramItem": {
            "parseStrategy": 0
        }
      },
      {
        "resource": "product-service",
        "count": 2,
        "grade": 1,
        "paramItem": {
            "parseStrategy": 0
        }
      }
    ]

配置完成以后启动网关项目,登录sentinel控制台,查看限流规则:

配置说明:

    以客户端IP作为限流因子
    public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0;
    以客户端HOST作为限流因子
    public static final int PARAM_PARSE_STRATEGY_HOST = 1;
    以客户端HEADER参数作为限流因子
    public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
    以客户端请求参数作为限流因子
    public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
    以客户端请求Cookie作为限流因子
    public static final int PARAM_PARSE_STRATEGY_COOKIE = 4;

限流测试

多次通过网关访问account-service服务进行测试 http://localhost:8090/account/getByCode/javadaily 查看限流效果:

自定义响应异常

SpringCloud-gateway限流异常默认的实现逻辑为SentinelGatewayBlockExceptionHandler,可以查看源码发现异常响应的关键代码如下

由于服务后端都是返回JSON的响应格式,所以我们需要修改原异常响应,将其修改成ResultData类的响应格式。要实现这个功能只需要写个新的异常处理器然后在SpringCloud GateWay配置类中注入新的异常处理器即可。

  • 自定义异常处理器CustomGatewayBlockExceptionHandler
    public class CustomGatewayBlockExceptionHandler implements WebExceptionHandler {
    	...
        /**
         * 重写限流响应,改造成JSON格式的响应数据
         * @author javadaily
         * @date 2020/1/20 15:03
         */
        private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
            ServerHttpResponse serverHttpResponse = exchange.getResponse();
            serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            ResultData<Object> resultData = ResultData.fail(ReturnCode.RC200.getCode(), ReturnCode.RC200.getMessage());
            String resultString = JSON.toJSONString(resultData);
            DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(resultString.getBytes());
            return serverHttpResponse.writeWith(Mono.just(buffer));
        }
    
        @Override
        public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
            if (exchange.getResponse().isCommitted()) {
                return Mono.error(ex);
            } else {
                return !BlockException.isBlockException(ex) ? Mono.error(ex) : this.handleBlockedRequest(exchange, ex).flatMap((response) -> this.writeResponse(response, exchange));
            }
        }
       ...
    }

大家可以直接复制 SentinelGatewayBlockExceptionHandler 类,然后修改 writeResponse方法接口

  • 修改Gateway配置类,注入CustomGatewayBlockExceptionHandler
    @Configuration
    public class GatewayConfiguration {
    	...
        /**
         * 注入自定义网关异常
         */
        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        public CustomGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
            // Register the custom block exception handler .
            return new CustomGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
        }
    	...
    }
  • 在bootstrap.yml文件中新增配置
    spring:
      main:
        allow-bean-definition-overriding: true
  • 重新测试,限流响应结果如下
    {
    	"message": "服务开启限流保护,请稍后再试!",
    	"status": 200,
    	"success": false,
    	"timestamp": 1579509123946
    }

限流不生效

各位在使用过程中如果发现网关层限流不生效,可以以debug模式启动网关服务,然后对网关过滤器 SentinelGatewayFilter 中的filter方法进行调试,我发现sentinel获取到的网关id并不是我们配置的account-service,而是加了CompositeDiscoveryClient_前缀,如下图所示:

所以我们需要修改 gateway-sentinel-flow 的配置,给我们的resource 也加上前缀,修改完的配置如下:

    [{
    	"resource": "CompositeDiscoveryClient_account-service",
    	"count": 5,
    	"grade": 1,
    	"paramItem": {
    		"parseStrategy": 0
    	}
    }, {
    	"resource": "CompositeDiscoveryClient_product-service",
    	"count": 2,
    	"grade": 1,
    	"paramItem": {
    		"parseStrategy": 0
    	}
    }]

通过使用jemter对接口进行测试,发现网关能正常限流

经过以上几步,我们可以将后端微服务层的限流配置去掉,让网关层承担限流的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值