Skip to content

限流规则

作者:唐亚峰 | battcn
字数统计:351 字

顾名思义,限流就是限制流量。通过限流,我们可以很好地控制系统的 QPS,从而达到保护系统的目的。 常见的限流算法有:计数器算法,漏桶(Leaky Bucket)算法,令牌桶(Token Bucket)算法。

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

添加依赖

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

添加配置

yaml
spring:
  application:
    name: wemirr-platform-gateway
  cloud:
    gateway:
      enabled: true
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      routes:
        - id: wemirr-platform-authority
          uri: lb://wemirr-platform-authority
          predicates:
            - Path=/authority/**
          filters:
            # 限流过滤器
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100   # 允许用户每秒处理多少个请求
                redis-rate-limiter.burstCapacity: 100   # 令牌桶的容量,允许在一秒钟内完成的最大请求数
                # 使用 IP 限流策略(使用 SpEL 按名称引用 bean)
                key-resolver: "#{@ipKeyResolver}"

实现代码

java
package com.wemirr.platform.gateway.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * @author Levin
 */
@Configuration
public class KeyResolverConfiguration {

    private static final String AUTHORIZATION = "authorization";


    /**
     * IP 限流
     *
     * @return IP限流
     */
    @Primary
    @Bean(name = "ipKeyResolver")
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName());
    }

    /**
     * 用户限流
     * 使用这种方式限流,请求路径中必须携带userId参数
     *
     * @return 用户限流
     */
    @Bean(name = "tokenKeyResolver")
    public KeyResolver tokenKeyResolver() {
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getHeaders().getFirst(AUTHORIZATION)));
    }

    /**
     * 接口限流
     * 获取请求地址的uri作为限流key
     *
     * @return 接口限流
     */
    @Bean(name = "apiKeyResolver")
    public KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }

}