spring gateway RequestRateLimiter

在使用spring-cloud-gateway时,能够配置请求限流;该限流基于redis实现, 用法以下java

spring:
   cloud:
     gateway:
       routes:
       -  id:requestratelimiter_route
         uri:http://example.org
         filters:
         -  name:RequestRateLimiter
           args: 
             redis-rate-limiter.replenishRate:10 #容许用户每秒执行多少请求,而不会丢弃任何请求。这是令牌桶填充的速率。
            redis-rate-limiter.burstCapacity:20 #一秒钟内容许执行的最大请求数。这是令牌桶能够容纳的令牌数。将此值设置为零将阻止全部请求。
            key-resolver: "#{@userkeyResolver}" #根据关键字标识的限流
@Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(RequestUtils.getIpAddress(exchange.getRequest()));
    }

以上配置是基于请求ip作的限流配置,容许每一个ip下一秒内能够有10个并请求,最多20个请求,但下一秒最多会变成10个请求;超过这个配置的请求将会被拒绝(返回409状态码)redis

源码分析:spring

源码位置在spring-cloud-gateway-core包下; 基于lua脚本实现缓存

local tokens_key = KEYS[1]    #请求惟一标识
local timestamp_key = KEYS[2] #请求时间

local rate = tonumber(ARGV[1])     #速率,如上面例子里的 10
local capacity = tonumber(ARGV[2]) #容量,如上面例子里的 20
local now = tonumber(ARGV[3])      #当前时间 
local requested = tonumber(ARGV[4])#请求数量,默认是1

local fill_time = capacity/rate    
local ttl = math.floor(fill_time*2) #获得过时时间

local last_tokens = tonumber(redis.call("get", tokens_key)) #剩余可用令牌,没有值则为桶的容量,上面例子里值范围是 0~20
if last_tokens == nil then
  last_tokens = capacity
end

local last_refreshed = tonumber(redis.call("get", timestamp_key)) #上次请求时间,没值则为0
if last_refreshed == nil then
  last_refreshed = 0
end

local delta = math.max(0, now-last_refreshed)     #单前时间与上次请求时间的差值,最小是0;
local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) #可用的令牌,最大为桶容量,范围是0~桶容量, 上面例子里是 0~20
local allowed = filled_tokens >= requested #是否容许请求, 可用令牌是否足够
local new_tokens = filled_tokens        
local allowed_num = 0
if allowed then
  new_tokens = filled_tokens - requested #可用令牌减去1
  allowed_num = 1
end

redis.call("setex", tokens_key, ttl, new_tokens) #缓存可用令牌
redis.call("setex", timestamp_key, ttl, now)     #缓存当前时间

return { allowed_num, new_tokens }

上面例子的入参状况是源码分析

相关文章
相关标签/搜索