在搭建前,我们需要了解一下springcloud-getway限流的策略:
有计算器算法、漏桶算法、令牌桶算法;
Spring Cloud Gateway限流
我搭建了两台服务器,一台是getway 一台是demo,我的想法是通过getway去调用demo服务器
getway 服务器搭建:
引入getway jar
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.study</groupId>
<artifactId>spring-cloud-getway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-getway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<!--getway网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--限流 redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
这里我们需要注意的地方是:
springboot的版本号和spring-boot-starter-data-redis-reactive的版本号;
我这里使用的是springboot 2.1.8 的,如果版本号是2.2以上的spring-boot-starter-data-redis-reactive的版本号也要做好调整不然启动会报错;
配置文件application.yml:
server:
port: 9000
spring:
main:
allow-bean-definition-overriding: true
cloud:
gateway:
routes:
- id: first_route #自定义id
uri: http://192.168.0.74:8080 #ָip转发服务器ip
predicates:
- Path=/demo/** #请求路径
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}"
application:
name: gateway-limiter
redis:
host: 192.168.0.75
port: 6379
password: 123456
database: 0
timeout: 1800000
配置RateLimiteConfig文件
package com.study.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")
);
}
}
从RateLimiteConfig 配置文件中可以清晰的看出,getaway的限流策略:对路径的限流,对ip限流,对参数限流。这些方式都可以在后面我们实现秒杀的时候其他一定的帮助;
为什么会引入redis呢?
在我们做压力测试的时候,并发量超过我们设置的阈值的时候,会把 错误信息存储到redis中
RequestRateLimiter是使用Redis来进行限流的,并在redis中存储了2个key
另一台服务demo搭建:
主要是接口路径:
我们可以清楚的看到getway服务器在application.yml中配置了
demo的服务器ip已经请求路径
限流配置:
这里采用的是令牌桶算法,配置方式都写了备注;