在使用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 }
上面例子的入参状况是源码分析