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 }

上面例子的入參狀況是源碼分析

相關文章
相關標籤/搜索