在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击
令牌算法
令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。
令牌桶算法的实现由Google Guava的RateLimiter、Bucket4j、RateLimitJ ,他们支持的 back-ends为JCache、Hazelcast、Redis ,您可以根据自己的技术栈选择相应的实现
限流维度
IP地址、用户(token)、URI地址
POM依赖
// 网关jar
<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</artifactId>
</dependency>
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: demo
uri: lb://demo
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
# 限流过滤器,使用gateway内置令牌算法
- name: RequestRateLimiter
args:
# 令牌桶每秒填充平均速率,即行等价于允许用户每秒处理多少个请求平均数
redis-rate-limiter.replenishRate: 50
# 令牌桶的容量,允许在一秒钟内完成的最大请求数
redis-rate-limiter.burstCapacity: 100
# 用于限流的键的解析器的Bean对象的名字
#它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
key-resolver: "#{@ipKeyResolver}"
import java.util.Objects;
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;
@Configuration
public class RequestRateLimiterConfig {
@Primary
@Bean
KeyResolver ipKeyResolver() {
// 按IP来限流
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress());
}
@Bean
KeyResolver apiKeyResolver() {
// 按URL限流
return exchange -> Mono.just(exchange.getRequest().getPath().toString());
}
@Bean
KeyResolver userKeyResolver() {
// 按用户限流
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getHeaders().getFirst("Authorization")));
}
}
以上就是代码层面的配置然后就可以实现服务级别的限流了
限流之后后端总要响应前端
后面就讲网关的熔断和服务降级