### POM 依赖 这里一定要注意,是网关引入的redis-reactive,背压模式的redis。 ``` <!--基于 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 ``` - 配置bean,多维度限流量的入口 对应上边key-resolver 【我自己哦 ``` /** * 自定义限流标志的key,多个维度可以从这里入手 * exchange对象中获取服务ID、请求信息,用户信息等 */ @Bean KeyResolver remoteAddrKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); } ``` OK 完成。 #### 压力测试 并发5个线程。 ![](https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901151618.png) #### Redis 数据变化 我们使用redis的**monitor** 命令,实时查看redis 的操作情况。 会发现在redis中会操作两个key - request_rate_limiter.{xxx}.timestamp - request_rate_limiter.{xxx}.tokens ![](https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901151633.png) ### 实现原理 ![](https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901151643.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))); } ``` #### LUA 脚本 ![](https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901151653.png)