微服务系列:服务网关 Spring Cloud Gateway 限流配置

3,342 阅读4分钟

上一篇文章 微服务系列:服务网关 Spring Cloud Gateway 入门 中我们学习了 Spring Cloud Gateway 的一些入门知识,今天我们来学习怎么使用 Spring Cloud Gateway 来实现限流。

话不多说,开始今天的学习。

限流配置

1. 相关概念

限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机。

常见的限流算法有:计数器算法,漏桶(Leaky Bucket)算法,令牌桶(Token Bucket)算法。

Spring Cloud Gateway官方提供了RequestRateLimiterGatewayFilterFactory过滤器工厂,使用Redis 和Lua脚本实现了 令牌桶 的方式。

令牌桶算法 是对漏桶算法的一种改进,漏桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。

放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌。所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

网上找了一个图:

image.png

2. 代码实战

上面已经提到过,Spring Cloud Gateway官方提供了RequestRateLimiterGatewayFilterFactory过滤器工厂,使用Redis 和Lua脚本实现了 令牌桶 的方式。

所以我们还需要引入 redis 的 reactive 依赖

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

application.yml 配置文件

server:
  port: 8080

spring:
  application:
    name: api-gateway
  redis:
    host: localhost
    port: 6379
    password:
  cloud:
    gateway:
      routes:
        - id: cloud-gateway
          uri: http://192.168.1.211:8088/
          predicates:
            - Path=/ytb/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1   # 令牌桶每秒填充速率
                redis-rate-limiter.burstCapacity: 2   # 令牌桶总容量
                key-resolver: "#{@pathKeyResolver}"   # 使用 SpEL 表达式按名称引用 bean

在上面的配置文件,配置了 redis 的信息,并配置了 RequestRateLimiter 的限流过滤器,该过滤器需要配置三个参数:

  • burstCapacity,令牌桶总容量。
  • replenishRate,令牌桶每秒填充平均速率。
  • key-resolver,用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据 #{@beanName} 从 Spring 容器中获取 Bean 对象。

编写URI限流规则配置类

@Configuration
public class KeyResolverConfiguration {
    @Bean
    public KeyResolver pathKeyResolver(){
        return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
    }
}

然后本地启动一个 redis,重启项目,我们就可以测试看看效果了

image.png

还是原来的接口,在我们快速刷新了几次之后会发现返回HTTP ERROR 429

image.png

说明我们的限流起了作用了,同时限流成功看到 redis 中维护了两个键

request_rate_limiter.{file/getFileType}.timestamp
request_rate_limiter.{xxx}.tokens

image.png

其他限流规则

参数限流:key-resolver: "#{@parameterKeyResolver}" (以下配置要求请求路径中必须携带 userId 参数)

@Bean
public KeyResolver parameterKeyResolver()
{
	return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}

IP限流:key-resolver: "#{@ipKeyResolver}"

@Bean
public KeyResolver ipKeyResolver()
{
	return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

过滤器规则(Filter)

过滤规则实例说明
PrefixPath- PrefixPath=/app在请求路径前加上app
RewritePath- RewritePath=/test, /app/test访问localhost:9022/test,请求会转发到localhost:8001/app/test
SetPathSetPath=/app/{path}通过模板设置路径,转发的规则会在路径前增加app,{path}表示原请求路径
RedirectTo重定向
RemoveRequestHeader去掉某个请求头信息

StripRefix

StripPrefix 就表明截取路径的个数

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: cloud-gateway
          uri: http://192.168.1.211:8088
          predicates:
            - Path=/ytb/my/**
          filters:
            - StripPrefix=2

这里就表示访问地址 http://localhost:8080/ytb/my/file/getFileType 会被转发到 http://192.168.1.211:8088/file/getFileType 地址。

跨域配置

spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOriginPatterns: "*"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true
            exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"

当然如果使用了 nginx 等配置代理来解决跨域,则可以不需要添加跨域支持。