spring cloud gateway默认基于redis令牌桶算法进行微服务的限流保护,采用RateLimter限流算法来实现。
1.引入依赖包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2、yml中配置redis
spring:
application:
name: mima-cloud-gateway
redis:
database: 1
host: localhost
port: 6379
password:
3、配置KeyResolver——RateLimiteConfig.java(接口限流/ip限流/用户限流)
package com.mkevin.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;
/**
* 限流配置KeyResolver——有三种写法(接口限流/ip限流/用户限流)
*/
@Configuration
public class RateLimiteConfig {
/**
* 接口限流:根据请求路径限流
* @return
*/
/*
如果不使用@Primary注解,会报如下错误,需要注意
Description:
Parameter 1 of method requestRateLimiterGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a single bean, but 3 were found:
- pathKeyResolver: defined by method 'pathKeyResolver' in class path resource [com/mkevin/gateway/config/RateLimiteConfig.class]
- ipKeyResolver: defined by method 'ipKeyResolver' in class path resource [com/mkevin/gateway/config/RateLimiteConfig.class]
- userKeyResolver: defined by method 'userKeyResolver' in class path resource [com/mkevin/gateway/config/RateLimiteConfig.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier
to identify the bean that should be consumed
*/
@Bean
@Primary
public KeyResolver pathKeyResolver() {
//写法1
return exchange -> Mono.just(
exchange.getRequest()
.getPath()
.toString()
);
/*
//写法2
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest()
.getPath()
.toString());
}
};
*/
}
/**
* 根据请求IP限流
* @return
*/
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest()
.getRemoteAddress()
.getHostName()
);
}
/**
* 根据请求参数中的userId进行限流
*
* 请求地址写法:http://localhost:8801/rate/123?userId=lisi
*
* @return
*/
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest()
.getQueryParams()
.getFirst("userId")
);
}
}
4、yml中配置spring.cloud.gateway.routes.filters
spring:
cloud:
gateway:
routes:
- id: rate-limit-demo
uri: lb://mima-cloud-producer
predicates:
#访问路径:http://localhost:8801/rate/123
- Path=/rate/**
filters:
- name: RequestRateLimiter
args:
# 令牌桶每秒填充平均速率, 允许用户每秒处理多少个请求。
redis-rate-limiter.replenishRate: 1
# 令牌桶的容量,允许在1s内完成的最大请求数。
redis-rate-limiter.burstCapacity: 2
# 使用SpEL表达式从Spring容器中获取Bean对象, 查看RateLimiteConfig实现类中的方法名
key-resolver: "#{@pathKeyResolver}"
#key-resolver: "#{@ipKeyResolver}"
#key-resolver: "#{@userKeyResolver}"
5、访问地址测试
http://localhost:8801/rate/123
当F5频繁刷新请求接口时,控制台会报429错误状态码,提示我们请求过多,如下:
[开始]请求路径:/rate/123
[应答]请求路径:/rate/123耗时:2ms
2020-09-08 16:23:27.253 DEBUG 18512 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] Completed 429 TOO_MANY_REQUESTS
2020-09-08 16:23:27.394 DEBUG 18512 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] HTTP GET "/rate/123"
corsFilter... run
[开始]请求路径:/rate/123
[应答]请求路径:/rate/123耗时:2ms
2020-09-08 16:23:27.397 DEBUG 18512 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] Completed 429 TOO_MANY_REQUESTS
2020-09-08 16:23:27.536 DEBUG 18512 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] HTTP GET "/rate/123"
corsFilter... run
6、当发生限流时,会向redis中存储两个数据
127.0.0.1:1>keys *
1) "request_rate_limiter.{/rate/123}.timestamp"
2) "request_rate_limiter.{/rate/123}.tokens"
127.0.0.1:1>keys *
1) "request_rate_limiter.{0:0:0:0:0:0:0:1}.timestamp"
2) "request_rate_limiter.{0:0:0:0:0:0:0:1}.tokens"
127.0.0.1:1>keys *
1) "request_rate_limiter.{lisi}.timestamp"
2) "request_rate_limiter.{lisi}.tokens"
参数说明:
request_rate_limiter.{key}.timestamp:
存储的是当前时间的秒数,也就是System.currentTimeMillis()/1000或者Instant.now().getEpochSecond()。
request_rate_limiter.{key}.tokens:
存储的是当前这秒钟对应的可用令牌数量
7、完整yml配置文件
# 开启resilience4j断路器
# spring.cloud.circuitbreaker.resilience4j.enabled: true
# 设置hystrix断路器超时时间
# hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 2000
spring:
application:
name: mima-cloud-gateway
redis:
database: 1
host: localhost
port: 6379
password:
cloud:
gateway:
routes:
- id: rate-limit-demo
uri: lb://mima-cloud-producer
predicates:
#访问路径:http://localhost:8801/rate/123
- Path=/rate/**
filters:
- name: RequestRateLimiter
args:
# 令牌桶每秒填充平均速率, 允许用户每秒处理多少个请求。
redis-rate-limiter.replenishRate: 1
# 令牌桶的容量,允许在1s内完成的最大请求数。
redis-rate-limiter.burstCapacity: 2
# 使用SpEL表达式从Spring容器中获取Bean对象, 查看RateLimiteConfig实现类中的同名方法
#key-resolver: "#{@pathKeyResolver}"
#key-resolver: "#{@ipKeyResolver}"
#请求地址写法:http://localhost:8801/rate/123?userId=lisi
key-resolver: "#{@userKeyResolver}"
# The RequestRateLimiter GatewayFilter Factory
# The Redis RateLimiter
# Modify a Request Body GatewayFilter Factory 测试版本,未来可能改动
# Modify a Response Body GatewayFilter Factory 测试版本,未来可能改动
server:
port: 8801
eureka:
client:
serviceUrl:
#defaultZone: http://kevin:123456@localhost:8761/eureka/
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
#instance-id: ${spring.application.name}:${spring.cloud.client.ip-address:}:${server.port}
instance-id: ${spring.application.name}:${server.port}
debug: true
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
shutdown: true