一、前言
Spring Cloud Gateway 内置 RequestRateLimiterGatewayFilterFactory 提供请求限流的功能。该 Filter 是基于 Token Bucket Algorithm(令牌桶算法)实现的限流,同时搭配上 Redis 实现分布式限流。
对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。如下图所示,令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
令牌桶算法示意图二、引入依赖
修改 [pom.xml
]文件,引入相关的依赖如下:
<dependencies>
<!-- 引入 Spring Cloud Gateway 相关依赖,使用它作为网关,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 实现对 Spring Data Redis 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
三、配置文件
修改 [application.yaml
]配置文件,增加限流 Filter 的配置项。完整配置如下:
server:
port: 8888
spring:
application:
name: sc-gateway-application
cloud:
## Spring Cloud Gateway 配置项,对应 GatewayProperties 类
gateway:
# 路由配置项,对应 RouteDefinition 数组
routes:
- id: erbadagang_rate_limiter # 路由的编号
uri: https://www.jianshu.com/u/ea0462d5074c # 路由到的目标地址(我的简书主页地址)
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- Path=/jianshu
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 # 令牌桶的每秒放的数量
redis-rate-limiter.burstCapacity: 2 # 令牌桶的最大令牌数
key-resolver: "#{@ipKeyResolver}" # 获取限流 KEY 的 Bean 的名字
- id: guo # 路由的编号
uri: https://www.126.com # 路由的目标地址
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- Path=/*
filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组
- StripPrefix=1
##### Redis 配置项 #####
redis:
host: 127.0.0.1
port: 6379
① 针对编号为 erbadagang_rate_limiter
的路由,我们在 filter
配置项,添加了限流过滤器 RequestRateLimiter
,其配置参数如下:
-
redis-rate-limiter.replenishRate
:令牌桶的每秒放的数量。 -
redis-rate-limiter.burstCapacity
:令牌桶的最大令牌数。burstCapacity
参数,我们可以近似理解为是每秒最大的请求数。因此每请求一次,都会从桶里获取掉一块令牌。
replenishRate
参数,我们可以近似理解为是每秒平均的请求数。假设在令牌桶为空的情况下,一秒最多放这么多令牌,所以最大请求书当然也是这么多。实际上,在令牌桶满的情况下,每秒最大的请求数是
burstCapacity + replenishRate
。 -
key-resolver
:获取限流 KEY 的 Bean 的名字。
② spring.redis
配置项,设置使用的 Redis 的配置。
有一点要注意,使用的 Redis 客户端需要提供 Reative 的操作,目前能够支持的是 Lettuce 和 Redisson,也就是说我们暂时不能使用 Jedis。
四、GatewayConfig
创建获取限流 KEY 的 Bean。代码如下:
package com.erbadagang.springcloud.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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @description 限流 KEY 的 Bean ,通过解析请求的来源 IP 作为限流 KEY,这样我们就能实现基于 IP 的请求限流。
* @ClassName: GatewayConfig
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/29 15:26
* @Copyright:
*/
@Configuration
public class GatewayConfig {
@Bean
public KeyResolver ipKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
// 获取请求的 IP
return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
};
}
}
创建的 ipKeyResolver Bean 是通过解析请求的来源 IP 作为限流 KEY,这样我们就能实现基于 IP 的请求限流。
如果说,我们想要实现基于用户的请求限流,那么我们可以创建从请求中解析用户身份的 KeyResolver Bean。也就是说,通过自定义的 KeyResolver 来实现不同粒度的请求限流。
五、 简单测试
- 启动Redis
- 执行 GatewayApplication, 启动网关代码。
- 使用浏览器,连续快速访问 http://127.0.0.1:8888/jianshu 地址,将会出现被限流为空白页。
底线
本文源代码使用 Apache License 2.0开源许可协议。可通过git clone
命令从代码的Gitee仓库地址获取代码到本地,或者直接点击链接通过浏览器方式查看源代码。
网友评论