SpringCloudGateway-网关限流实现

87 阅读2分钟

1.概述

令牌桶算法是比较常见的限流算法之一,大概描述如下:

1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;

2)根据限流大小,设置按照一定的速率往桶里添加令牌;

3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;

4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;

5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流

image.png

2.基于Filter的限流实现

SpringCloudGateway官方就提供了基于令牌桶的限流支持。基于其内置的过滤器工厂RequestRateLimiterGatewayFilterFactory 实现。在过滤器工厂中是通过Redis和lua脚本结合的方式进行流量控制。

2.1 环境搭建

①:准备 redis

准备一个redis的服务端,本地即可

②:在网关服务中导入 redis的依赖

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

2.2 修改application.yml配置文件

在application.yml配置文件中加入限流和redis链接的配置,代码如下:

server:
  port: 80

spring:
  redis:
    host: localhost
    port: 6379
  application:
    name: api-gateway-server

  cloud:
    # 网关配置
    gateway:
      - id: gateway-consumer
        # uri: http://localhost:9000
        uri: lb://GATEWAY-CONSUMER
        predicates:
        - Path=/order/**
        filters:
          - name: RequestRateLimiter
            args:
              # 使用SpEL从容器中获取对象
              key-resolver: '#{@pathKeyResolver}'
              # 令牌桶每秒填充平均速率
              redis-rate-limiter.replenishRate: 1
              # 令牌桶的上限
              redis-rate-limiter.burstCapacity: 3
        # 微服务名称配置
      discovery:
        locator:
          enabled: true # 设置为true 请求路径前可以添加微服务名称
          lower-case-service-id: true # 允许为小写


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

以上的配置中:

表示 1秒内,允许 3个请求通过,令牌桶的填充速率也是一秒钟添加一个令牌。 最大突发状况 也只允许 一秒内有3次请求,可以根据业务来调整 。

2.3 配置KeyResolver

为了达到不同的限流效果和规则,可以通过实现 KeyResolver 接口,定义不同请求类型的限流键。

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
​
@Configuration
public class PathKeyResolverConfig {
​
    @Bean
    public KeyResolver pathKeyResolver() {
        return new KeyResolver() {
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                //根据ip进行限流
                return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
                //根据请求路径进行限流
//                return Mono.just(exchange.getRequest().getPath().toString());
                //根据参数进行限流
//                return Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
            }
        };
    }
}