参考:
API Rate Limiting with Spring Cloud Gateway
Spring Microservices Security Best Practices

示例配置

按照key-resolver解析出的id(字符串key,用于唯一区分用户、IP等等)进行请求限流,
限流算法采用基于redis lua脚本实现的令牌桶算法,具体配置见如下说明:

server:port: 8088
spring:application:name: mx-gateway-optcloud:# 网关配置gateway:# 默认过滤器(对所有route均生效)default-filters:# 请求限速配置- name: RequestRateLimiterargs:# 如果keyResolver返回空key,则拒绝该请求403,默认true表示拒绝,false则表示允许访问deny-empty-key: false# 令牌桶算法每秒补充的token数量(每秒的请求数量)redis-rate-limiter.replenishRate: 10# 令牌桶算法token最大数量(每秒的最大请求数量)redis-rate-limiter.burstCapacity: 15# 单次请求消费的token数量redis-rate-limiter.requestedTokens: 1# 自定义的KeyResolver(从请求exchange解析id,用于区分限流的独立单元,如用户ID、remoteAddr、sessionId等)key-resolver: "#{@begRateLimiterKeyResolver}"# redis配置redis:database: 2host: localhostpassword: mypasswport: 6379timeout: 3000lettuce:pool:max-active: 8max-idle: 8max-wait: -1min-idle: 0

keyresovler默认实现

org.springframework.cloud.gateway.filter.ratelimit.PrincipalNameKeyResolver

package org.springframework.cloud.gateway.filter.ratelimit;
import reactor.core.publisher.Mono;
import org.springframework.web.server.ServerWebExchange;
public class PrincipalNameKeyResolver implements KeyResolver {/*** {@link PrincipalNameKeyResolver} bean name.*/public static final String BEAN_NAME = "principalNameKeyResolver";@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {return exchange.getPrincipal().flatMap(p -> Mono.justOrEmpty(p.getName()));}}

RequestRateLimiterGatewayFilterFactory

RequestRateLimiterGatewayFilterFactory调用链路
RequestRateLimiterGatewayFilterFactory
-> RedisRateLimiter -> META/scripts/request_rate_limter.lua

request_rate_limiter.lua
lua脚本保证redis单线程执行,解决分布式网关限流问题(避免多个网关同时调用redis导致数据混乱)

--keys参数:
--request_rate_limiter.{id}.tokens
--request_rate_limiter.{id}.timestamp
local tokens_key = KEYS[1]
local timestamp_key = KEYS[2]
--redis.log(redis.LOG_WARNING, "tokens_key " .. tokens_key)--对应配置:
--redis-rate-limiter.replenishRate: 每秒需要补充的token数量
--redis-rate-limiter.burstCapacity: 令牌桶中token的最大容量
--当前时间戳(秒)
--redis-rate-limiter.requestedTokens: 单次请求消耗的token数量
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])--填充时间(即需要几秒即可将令牌桶填充满)
local fill_time = capacity/rate
--redis缓存到期时间(2倍且向下取整),
--即超过ttl时间没人访问(则redis缓存tokens_key, timestamp_key失效不存在),
--则默认令牌桶为满的状态(capacity容量)
local ttl = math.floor(fill_time*2)--redis.log(redis.LOG_WARNING, "rate " .. ARGV[1])
--redis.log(redis.LOG_WARNING, "capacity " .. ARGV[2])
--redis.log(redis.LOG_WARNING, "now " .. ARGV[3])
--redis.log(redis.LOG_WARNING, "requested " .. ARGV[4])
--redis.log(redis.LOG_WARNING, "filltime " .. fill_time)
--redis.log(redis.LOG_WARNING, "ttl " .. ttl)--获取之前(上次访问后)剩余的token数量(默认capacity)
local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil thenlast_tokens = capacity
end
--redis.log(redis.LOG_WARNING, "last_tokens " .. last_tokens)--获取上次访问的时间戳(默认为0)
local last_refreshed = tonumber(redis.call("get", timestamp_key))
if last_refreshed == nil thenlast_refreshed = 0
end
--redis.log(redis.LOG_WARNING, "last_refreshed " .. last_refreshed)--距离上次访问已经过去的秒数
local delta = math.max(0, now-last_refreshed)
--当前时间点补充后的token数量(默认capacity,且最多不操作capacity)
--之前剩下的token数量+上次访问后到现在为止需要添加的token数量delta*rate
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
--是否允许访问(补充后的token数量 >= 当前单次请求消耗的token数量)
local allowed = filled_tokens >= requested
--补充后的token数量
local new_tokens = filled_tokens
--是否允许访问
local allowed_num = 0
--若允许访问
if allowed then--当前请求过后剩余的token数量=补充后的token数量-当前请求消耗的token数量new_tokens = filled_tokens - requested--标记允许访问allowed_num = 1
end--redis.log(redis.LOG_WARNING, "delta " .. delta)
--redis.log(redis.LOG_WARNING, "filled_tokens " .. filled_tokens)
--redis.log(redis.LOG_WARNING, "allowed_num " .. allowed_num)
--redis.log(redis.LOG_WARNING, "new_tokens " .. new_tokens)--记录令牌桶tokens_key, timestamp_key
--超过ttl(需要几秒即可填充满令牌桶)后缓存失效,key不存在则默认为满的状态
--例如rate=100, capacity=150, 则ttl=Math.floor((150/100)*2)=3,
--即超过3秒没人访问,则令牌桶会自动被填充满(key失效为空则表示满状态)
if ttl > 0 thenredis.call("setex", tokens_key, ttl, new_tokens)redis.call("setex", timestamp_key, ttl, now)
end-- return { allowed_num, new_tokens, capacity, filled_tokens, requested, new_tokens }
return { allowed_num, new_tokens }

lua脚本调用参数debug截图

redis缓存截图

限流相关的响应header

# burst容量(令牌桶1秒时间内最大容量)
X-RateLimit-Burst-Capacity: 1
# 当前1秒内剩余token数据量
X-RateLimit-Remaining: 0
# 每秒补充的token数量
X-RateLimit-Replenish-Rate: 1
# 当前请求需要消费的token数量
X-RateLimit-Requested-Tokens: 1

SpringCloud Gateway RequestRateLimiter相关推荐

  1. SpringCloud gateway (史上最全)

    前言 疯狂创客圈(笔者尼恩创建的高并发研习社群)Springcloud 高并发系列文章,将为大家介绍三个版本的 高并发秒杀: 一.版本1 :springcloud + zookeeper 秒杀 二.版 ...

  2. SpringCloud Gateway 通过redis实现限流

    前言 在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击. 常见的限流方式,比如Hystrix适用线程池隔离,超过线程池的负载 ...

  3. 【SpringCloud】04 网关springcloud gateway

    网关springcloud gateway 上面的架构,会存在着诸多的问题: 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性 认证复杂,每个服务都需要独立认证. 存在跨域请求,在一定场景 ...

  4. SpringCloud Gateway 测试问题解决

    SpringCloud Gateway 测试问题解决 参考文章: (1)SpringCloud Gateway 测试问题解决 (2)https://www.cnblogs.com/dalaoyang/ ...

  5. Springboot 集成Springcloud gateway的入门

    最近做项目使用到了springcloude gateway作为网关,因此在此记录下springcloud gateway的入门操作,后续再将源码解读写出来,先立个flag. 回归正题,Springcl ...

  6. 使用springcloud gateway搭建网关(分流,限流,熔断)

    Spring Cloud Gateway Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 ...

  7. SpringCloud Gateway的工作方式

    SpringCloud Gateway工作方式 从官网的图来看,并不是特别复杂,首先客户端请求都会先经过Gateway Handler Mapping,匹配上就通过Gateway Web Handle ...

  8. SpringCloud Gateway的组成结构

    SpringCloud Gateway结构 SpringCloud Gateway的底层基于Netty,主要组成有Predicates(谓词或者断言).Route(路由).Filter(过滤器) 思维 ...

  9. springcloud gateway 自定义 accesslog elk

    大家好,我是烤鸭: ​ 最近用 springcloud gateway 时,想使用类似 logback-access的功能,用来做数据统计和图表绘制等等,发现没有类似的功能,只能自己开发了. 环境: ...

最新文章

  1. 微生物组助手——最易学的扩增子、宏基因组分析流程
  2. #define、#undef、#ifdef、#ifndef、#if、#elif、#else、#endif、defined解释
  3. JavaScript学习 九、事件
  4. mysql插入ㄖ_原生JavaScript代码100个实例
  5. svm训练完保存权重_assignment1-SVM
  6. 开发基础之使用git把项目提交到github托管
  7. 选择Java密码算法第1部分-散列
  8. 程序员,其实你可以做的更好
  9. @Entity,@Indexed @XmlRootElement
  10. 竞彩足球混合过关赔率API调用示例代码
  11. Java日常·面对对象
  12. 微博中微服务缓存_新浪微博温情:基于微服务的微博直播互动架构设计经验分享...
  13. www.etiger.vip DEVC++练习(入门)
  14. Remote Sensing投稿经历
  15. Vim 匹配相同的单词并高亮
  16. Python网络爬虫之Xpath详解
  17. 浪漫是浪漫,不浪漫也是浪漫
  18. Hive collect_set函数
  19. PHP程序员的工作内容复杂吗?如何更快完成交代的任务?
  20. 华翼宽带android客户端,实地测试 看华翼宽带是否带宽缩水_网络设备评测-中关村在线...

热门文章

  1. 【ffmpeg基础】ffmpeg视频编码
  2. 安装tomcat的详细步骤
  3. oracle如何kill锁中的sid,杀掉Oracle进程中的死锁的具体方案。
  4. qboost怎么设置_qboost怎么用 教你设置最好方法,但是很多人下载过来不
  5. 液压系统管路流速推荐表_液压系统的几种管路
  6. python爬取内容和f12不一致_爬取页面和审查元素获取的内容不一致
  7. 062卫星发射时刻时间归化
  8. Window XP驱动开发(二十一) 过滤驱动程序
  9. IDEA启动Tomcat之后不能访问localhost的解决方案
  10. python:接口开发