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

本贴最后更新于 1726 天前,其中的信息可能已经时移世异

导读:通过前面的章节我们在微服务层做了限流,并且集成了 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-idgateway-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 控制台,查看限流规则:
image.png

配置说明:

以客户端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 查看限流效果:
image.png

自定义响应异常

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

由于服务后端都是返回 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_ 前缀,如下图所示:
image.png

所以我们需要修改 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
	}
}]

image.png

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

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

好了,各位朋友们,本期的内容到此就全部结束啦,能看到这里的同学都是优秀的同学,下一个升职加薪的就是你了!
如果觉得这篇文章对你有所帮助的话请扫描下面二维码加个关注。

"转发" 加 "在看",养成好习惯!咱们下期再见!

SpringCloud Alibaba 系列文章

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    944 引用 • 1459 回帖 • 17 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖 • 1 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    142 引用 • 442 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...