一 背景
- spring cloud gateway自带到限流过滤器无法满足我们的需求。
- 1
RequestRateLimiterGatewayFilterFactory
不支持指定uri - 2
RequestRateLimiterGatewayFilterFactory
不会具体到route - 3
RequestRateLimiterGatewayFilterFactory
不支持指定速率单位 - 4
RequestRateLimiterGatewayFilterFactory
不支持指定响应body二 第一步-部分重写RedisRateLimiter
1
2
3
4
5
6import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import reactor.core.publisher.Mono;
public interface CustomRateLimiter<C> extends RateLimiter<C> {
Mono<RateLimiter.Response> isAllowed(CustomRequestRateLimiterGatewayFilterFactory.Config customConfig, String id);
}
1 | import org.apache.commons.logging.Log; |
1 |
|
三 第二步-部分重写lua脚本,支持指定速率单位per_seconds
META-INF/scripts/request_rate_limiter.lua1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57local tokens_key = KEYS[1]
local timestamp_key = KEYS[2]
--redis.log(redis.LOG_WARNING, "tokens_key " .. tokens_key)
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
local per_seconds = tonumber(ARGV[5])
local fill_time = capacity/rate
local ttl = math.floor(fill_time*per_seconds*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)
local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil then
last_tokens = capacity
end
--redis.log(redis.LOG_WARNING, "last_tokens " .. last_tokens)
local last_refreshed = tonumber(redis.call("get", timestamp_key))
if last_refreshed == nil then
last_refreshed = 0
end
--redis.log(redis.LOG_WARNING, "last_refreshed " .. last_refreshed)
local add_delta = math.floor((now-last_refreshed)/per_seconds)
local delta = math.max(0, add_delta)
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
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)
redis.call("setex", tokens_key, ttl, new_tokens)
if delta > 0 then
redis.call("setex", timestamp_key, ttl, now)
else
redis.call("setex", timestamp_key, ttl, last_refreshed)
end
return { allowed_num, new_tokens }
四 第三步-重写RequestRateLimiterGatewayFilterFactory
1 | /** |
1 | import org.springframework.http.HttpStatus; |
1 | import lombok.Data; |
1 | import org.springframework.http.HttpStatus; |
1 | package com.xx.mall.gateway.ratelimiter; |
1 |
|
五 route网关配置示例
1 | - id: products |