20, Spring cloud Gateway 原生支持接口

2019-02-12  本文已影响63人  滔滔逐浪

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个线程

625766066-5b38c5499d478_articlex.png

Redis数据变化
我们使用redis的monitor命令,实时查看redis的操作情况
会发现在redis中会操作2个key
request_rate_limiter.{xxx}.timestamp
request_rate_limiter.{xxx}.tokens

2148678167-5b38c54a746ba_articlex.png

实现原理

625766066-5b38c5499d478_articlex.png

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)));
}
1496499253-5b38c549a9760_articlex.png
上一篇下一篇

猜你喜欢

热点阅读