SpringCloud微服务五:Springcloudgateway限流
前⾔:在互联⽹应⽤中,特别是电商,⾼并发的场景⾮常多,⽐如:秒杀、抢购、双11等,在开始时间点会使流量爆发式地涌⼊,如果对⽹络流量不加控制很有可能造成后台实例资源耗尽。限流是指通过指定的策略削减流量,使到达后台实例的请求在合理范围内。本章将介绍spring cloud gateway如何实现限流。
前情回顾请参考:
限流算法
主流的限流算法有两种:漏桶(leaky bucket)和令牌桶(token bucket)。漏桶算法有⼀个固定容量的桶,对于流⼊的⽔
⽆法预计速率,流出的⽔以固定速率,当⽔满之后会溢出。
令牌桶算法,有⼀个固定容量的桶,桶⾥存放着令牌(token)。桶最开始是空的,token以⼀个固定速率向桶中填充,直到达到桶的容量,多余的token会被丢弃。每当⼀个请求过来时,都先去桶⾥取⼀个token,如果没有token的话请求⽆法通过。
两种算法的最主要区别是令牌桶算法允许⼀定流量的突发,因为令牌桶算法中取⾛token是不需要时间的,
即桶内有多少个token都可以瞬时拿⾛。基于这个特点令牌桶算法在互联⽹企业中应⽤⽐较⼴泛,我们在实现限流的时候也会基于这个算法。
gateway如何实现限流
⽅法1:Spring cloud gateway实现限流的⽅式主要是通过添加⾃定义filter来实现,⾃定义filter需要实现GatewayFilter和Ordered接⼝。本章将结合开源的Bucket4j来实现,Bucket4j是基于令牌桶算法实现,Bucket4j代码参考:github/vladimir-
bukhtoyarov/bucket4j
⾸先修改api-gateway module,pom中添加Bucket4j依赖,最新版本是4.3.0,⼯程的版本已经在⽗⼯程中定义好了
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
</dependency>
第⼆步,添加filter实现GatewayFilter和Ordered,添加相应的参数,并使⽤⼀个ConcurrentHashMap存储ip以及bucket,实现filter ⽅法,对客户端访问ip进⾏过滤
public class LimitFilter implements GatewayFilter, Ordered {
private static final Logger logger = Logger(LimitFilter.class);
private int capacity;
private int refillTokens;
private Duration refillDuration;
public LimitFilter(int capacity, int refillTokens, Duration refillDuration) {
this.capacity = capacity;
}
private static final Map<String, Bucket> CACHE = new ConcurrentHashMap<>();
private Bucket createNewBucket() {
Refill refill = dy(refillTokens, refillDuration);
Bandwidth limit = Bandwidth.classic(capacity, refill);
return Bucket4j.builder().addLimit(limit).build();
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String ip = Request().getRemoteAddress().getAddress().getHostAddress();
Bucket bucket = CACHEputeIfAbsent(ip, k -> createNewBucket());
logger.info("IP: "+ip+", available tokens :"+AvailableTokens());
if (Consume(1L)) {
return chain.filter(exchange);
}
logger.info("IP: "+ip+", available tokens :"+AvailableTokens()+" too many requests");
Response().setComplete();
}
@Override
public int getOrder() {
return 0;
}
}
第三步,添加⾃定义路由,添加配置类,RouteLocator构造器中添加filter以及相应的地址信息,设置同⼀ip同时只能访问⼀次,多余的将被忽略。另外,由于我们在程序中配置了路由,需要将l中的gateway相关属性删除。
@Configuration
public class RouteLocatorConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
System.out.println("============================RouteLocatorConfig "+ utes());
return builder
.routes()
.route(r -> r.path("/*")
springcloud和springboot.filters(f -> f.filter(new LimitFilter(1,
1, Duration.ofSeconds(1))))
.uri("localhost:10080/")
.order(0)
.id("user_route"))
.build();
}
}
⽅法2:使⽤spring cloud 原⽣的redis⽅式
    第⼀步,搭建redis服务器,具体⽅法参考redis官⽹
    第⼆步,pom中添加redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
    ⾃定义resolver
@Configuration
public class CustomResolver {
@Bean
public KeyResolver ipKeyResolver(){
System.out.println(">>####ipKeyResolver>>>>####");
return exchange -> Mono.Request().getRemoteAddress().getHostName());
}
}
  第三步,修改l配置
routes:
- id: user_route
uri: localhost:10080
predicates:
- Path=/*
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.burstCapacity: 1
key-resolver: "#{@ipKeyResolver}
最后做测试,并使⽤monitor命令监控redis

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。

发表评论