spring cloud gateway(實現限流)
阿新 • • 發佈:2018-12-09
spring cloud gateway(實現限流)
限流一般有兩個實現方式,令牌桶和漏桶
令牌桶是初始化令牌(容器)的個數,通過拿走裡邊的令牌就能通過, 沒有令牌不能報錯,可以設定向容器中增加令牌的速度和最大個數
漏桶是向裡邊放入請求,當請求數量達到最大值後,丟棄,漏桶中的資料以一定速度流出,沒有則不流出
令牌桶實現方式如下:
pom
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> <version>4.0.0</version> </dependency>
建立下邊類並且繼承下邊類
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; /** * @Description: 自定義過濾器進行限流 * @Author: zzh * @Modified By: * @Date: 2018/12/3 18:07 */ public class GatewayRateLimitFilterByIP implements GatewayFilter, Ordered { private final Logger log = LoggerFactory.getLogger(GatewayRateLimitFilterByIP.class); /** * 單機閘道器限流用一個ConcurrentHashMap來儲存 bucket, * 如果是分散式叢集限流的話,可以採用 Redis等分散式解決方案 */ private static final Map<String, Bucket> LOCAL_CACHE = new ConcurrentHashMap<>(); /** * 桶的最大容量,即能裝載 Token 的最大數量 */ int capacity; /** * 每次 Token 補充量 */ int refillTokens; /** *補充 Token 的時間間隔 */ Duration refillDuration; public GatewayRateLimitFilterByIP() { } /** * * @param capacity 即能裝載 Token 的最大數量. * @param refillTokens * @param refillDuration */ 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<Void> 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<String, Bucket> 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();