20, Spring cloud Gateway 原生支持接口限流怎麼玩(轉)

Spring Cloud Gateway 原生支持接口限流該怎麼玩

關於Spring Cloud Gateway
SpringCloudGateway是spring官方基於Spring5.0,Spring Boot2.0 和Project Reactor等技術開發的網關,Spring雲網關旨在提供一種簡單高效的路由API的方法。Spring cloud 生態系的網關。目標是替代Netflix ZUUL, 其不僅提供統一的路由方式,並且基於Filter鏈的方式提供了網關的基本功能,例如: 安全,監控/埋點,和限流等
Zuul:構建高可用網關之多維度限流

開始Gateway的限流
pom依賴

<!--spring cloud gateway依賴-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--基於 reactive stream 的redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

按照請求的ip限流

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: lb://pigx-upms
        order: 10000
        predicates:
        - Path=/admin/**
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 1  # 令牌桶的容積
            redis-rate-limiter.burstCapacity: 3  # 流速 每秒
            key-resolver: "#{@remoteAddrKeyResolver}" #SPEL表達式去的對應的bean
        - StripPrefix=1


所有關於admin 的請求都會轉發過來 , - StripPrefix=1 去掉admin 如
http://localhost:8080/admin/api/user => http://pigx-upms:8081/api/user


配置bean
在啓動類配置

/**
*自定義限流標誌的key,多個維度可以從這裏入手
*exchange對象中獲取服務ID,請求信息,用戶信息等
*/
@Bean
KeyResolver remoteAddrkeyResolver(){
return exchange -> Mono.just(
exchange.getRequest.getRemoteAddress().getHostName());


}

ok完成
壓力測試
開發5個線程

Redis數據變化
我們使用redis的monitor命令,實時查看redis的操作情況
會發現在redis中會操作2個key
request_rate_limiter.{xxx}.timestamp
request_rate_limiter.{xxx}.tokens

實現原理

spring cloud Gateway 默認實現Redis限流,如果擴展只需要實現ratelimter接口既可

RedisRateLimter 的核心代碼,判斷是否取到令牌的實現,通過調用 redis的LUA 腳本。

public Mono<Response> isAllowed(String routeId, String id) {
    Config routeConfig = getConfig().getOrDefault(routeId, defaultConfig);
    int replenishRate = routeConfig.getReplenishRate();
    int burstCapacity = routeConfig.getBurstCapacity();

    try {
        List<String> keys = getKeys(id);
        returns unixtime in seconds.
        List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "",
                Instant.now().getEpochSecond() + "", "1");
        // 這裏是核心,執行redis 的LUA 腳本。
        Flux<List<Long>> flux =
        this.redisTemplate.execute(this.script, keys, scriptArgs);
        return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L)))
                .reduce(new ArrayList<Long>(), (longs, l) -> {
                    longs.addAll(l);
                    return longs;
                }) .map(results -> {
                    boolean allowed = results.get(0) == 1L;
                    Long tokensLeft = results.get(1);

                    Response response = new Response(allowed, getHeaders(routeConfig, tokensLeft));

                    if (log.isDebugEnabled()) {
                        log.debug("response: " + response);
                    }
                    return response;
                });
    }
    catch (Exception e) {
        log.error("Error determining if user allowed from redis", e);
    }
    return Mono.just(new Response(true, getHeaders(routeConfig, -1L)));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章