在使用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 }
上面例子的入參狀況是源碼分析