限流一般有兩個實作方式,令牌桶和漏桶
需要JAVA Spring Cloud大型企業分布式微服務雲建構的B2B2C電子商務平台源碼:壹零叁八柒柒肆六二六
令牌桶是初始化令牌(容器)的個數,通過拿走裡邊的令牌就能通過, 沒有令牌不能報錯,可以設定向容器中增加令牌的速度和最大個數
漏桶是向裡邊放入請求,當請求數量達到最大值後,丢棄,漏桶中的資料以一定速度流出,沒有則不流出
令牌桶實作方式如下:
pom
com.github.vladimir-bukhtoyarov
bucket4j-core
4.0.0
建立下邊類并且繼承下邊類
package com.gla.datacenter.filter;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class GatewayRateLimitFilterByIP implements GatewayFilter, Ordered {
private final Logger log = LoggerFactory.getLogger(GatewayRateLimitFilterByIP.class);
private static final Map LOCAL_CACHE = new ConcurrentHashMap<>();
int capacity;
int refillTokens;
Duration refillDuration;
public GatewayRateLimitFilterByIP() {
}
public GatewayRateLimitFilterByIP(int capacity, int refillTokens, Duration refillDuration) {
this.capacity = capacity;
this.refillTokens = refillTokens;
this.refillDuration = refillDuration;
}
private Bucket createNewBucket() {
Refill refill = Refill.of(refillTokens, refillDuration);
Bandwidth limit = Bandwidth.classic(capacity, refill);
return Bucket4j.builder().addLimit(limit).build();
}
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
//若ip不存在則建立一個Bucket(令牌桶)
Bucket bucket = LOCAL_CACHE.computeIfAbsent(ip, k -> createNewBucket());
log.info("IP:{} ,令牌通可用的Token數量:{} " ,ip,bucket.getAvailableTokens());
if (bucket.tryConsume(1)) {
return chain.filter(exchange);
} else {
//當可用的令牌書為0是,進行限流傳回429狀态碼
log.error("IP:{} ,限制通路:{} " ,ip,bucket.getAvailableTokens());
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
}
@Override
public int getOrder() {
return -1000;
}
public static Map getLocalCache() {
return LOCAL_CACHE;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public int getRefillTokens() {
return refillTokens;
}
public void setRefillTokens(int refillTokens) {
this.refillTokens = refillTokens;
}
public Duration getRefillDuration() {
return refillDuration;
}
public void setRefillDuration(Duration refillDuration) {
this.refillDuration = refillDuration;
}
}
配置路由
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//生成比目前時間早一個小時的UTC時間
ZonedDateTime minusTime = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route(r ->r.path("/demo/**")
//過濾器
.filters(f -> f.filter(new APIGatewayFilter())
.filter(new GatewayRateLimitFilterByIP(10,1, Duration.ofSeconds(1))))
.uri("http://192.168.26.113:8001/demo").order(0).id("demo_route"))
.route(r ->r.path("/test")
.uri("http://192.168.26.113/system/nav/login").id("jd_route")
)
build();
}