文章目录
- 前言
- 一、如何限流?
- 二、使用步骤
- 总结
前言
限流的意义
限流是针对于并发量比较高的时候,如果不针对对应的服务做限流操作,可能造成服务器压力过大,宕机等情况.
一、如何限流?
限流的方式:
- 计数器算法(Counter)
–设计一个计数器,比如一个全局的变量,每次请求后+1,并且在限定时间内比如一分钟,将计数器重置一次。当每次请求时查看计数器是否已经为临界值了,是就限流。但是这个有个缺点就是比如在55秒前没有请求,在55-70秒时有20000次请求,而计数器的临界值则是10000,此时的在60秒时清空了一次,这20000次的请求也是可以进来的。
- 漏桶算法(Leaky Bucket)
–就是所有的请求都放到gateway中,然后再去一个一个分发下去到对应的服务,这样做的缺点就是在大量数据的请求下可能gateway根本无法承受,而下游的服务依然在空闲当中或是毫无压力。
- 令牌桶算法(Token Bucket)
–设计一定数量的令牌,每次请求都会取一个令牌,并且令牌桶会根据规则自动生成令牌。大量请求过来时超过了限定的值,桶里的令牌瞬间被抢空,剩下没有拿到令牌的请求将会失败,并且桶中的令牌一直是有序增加的剩下的请求也能抢到。目前这是一种最优的解决方案。
二、使用步骤
Gateway的令牌桶算法实现:
Gateway中的限流算法就是采用了令牌桶算法,支持三种令牌桶算法:基于URI限流、基于请求参数限流、基于IP限流。
- 引入依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--redis支持-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
- 添加配置信息
server:port: 9003
eureka:client:fetch-registry: true # 从 eureka 服务端获取注册信息register-with-eureka: true # 将自身注册到 eureka 服务端service-url:defaultZone: http://localhost:9000/eurekainstance:prefer-ip-address: true # 开启采用 IP 注册形式
spring:application:name: flowershop-gatewaycloud:gateway:routes:- id: gateway-flower #路由id,唯一uri: lb://flowershop-common #路由地址,针对哪个服务的路由predicates: #断言- Path=/** filters:# 指定限流过滤器- name: RequestRateLimiterargs:# 基于令牌桶算法,生成令牌的速率redis-rate-limiter.replenishRate: 1# 令牌桶的最大容量redis-rate-limiter.burstCapacity: 1# 指定生成令牌的算法解析策略,这里是使用了SpEL表达式,获取寻找名字是 keyResolver 的bean对象key-resolver: "#{@keyResolver}"redis:host: 192.168.3.52database: 0port: 6379password:jedis:pool:max-idle: 100max-wait:min-idle: 5timeout: 500
- 令牌算法
Gateway中有多种限流策略,通过URI进行限流、通过请求参数限流、通过IP地址限流,但是我们只可以去实现其中一种。可以去实现 KeyResolver 接口
可以有多种方式实现:在启动类中实现、使用配置类实现
这里我们使用配置类
/*** 令牌桶算法中,令牌的生成算法*/
@Configuration
public class KeyResolverConfiguration{@Beanpublic KeyResolver keyResolver() {return new KeyResolver() {@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {// 这里根据请求【URI】进行限流return Mono.just(exchange.getRequest().getPath().toString());}};}// @Bean
// public KeyResolver keyResolver() {
// return new KeyResolver() {
// @Override
// public Mono<String> resolve(ServerWebExchange exchange) {
// // 这里根据请求【请求参数username】进行限流
// return Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
// }
// };
// }
//
// @Bean
// public KeyResolver keyResolver() {
// return new KeyResolver() {
// @Override
// public Mono<String> resolve(ServerWebExchange exchange) {
// // 这里根据请求【IP地址】进行限流
// return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
// }
// };
}
总结
一般情况下,项目中都会用到redis作为缓存,既然这样,我们完全可以用redis做gateway限流处理,可以减少sential插件的引入以及学习成本,何不美哉~