我所实现的 rate limiting 是基于How to implement rate limiting using Redis - Stack Overflow的,rateLimiting.lua
如下:
-- https://stackoverflow.com/questions/13175050/how-to-implement-rate-limiting-using-redis
-- KEYS[1] = rateLimitingKey, ARGV[1] = timePeriodInSeconds, ARGV[2] = allowableNumberOfCalls, ARGV[3] = nowEpochSecond
local timePeriodInSeconds = tonumber(ARGV[1])
local allowableNumberOfCalls = tonumber(ARGV[2])
local nowEpochSecond = tonumber(ARGV[3])
local times = redis.call('RPUSH', KEYS[1], nowEpochSecond)
if times > allowableNumberOfCalls then
local timeStart = redis.call('LINDEX', KEYS[1], 0)
local timeEnd = redis.call('LINDEX', KEYS[1], -1)
redis.call('LTRIM', KEYS[1], -allowableNumberOfCalls, -1)
if timeEnd - timeStart <= timePeriodInSeconds then
return false
else
return true
end
else
return true
end
如果 times <= allowableNumberOfCalls ,直接返回 true ,如果 times > allowableNumberOfCalls ,那么判断一下 timeEnd - timeStart <= timePeriodInSeconds ,如果结果为 true ,那么 return false ,否则 return true 。
但是这有个问题,因为 nowEpochSecond 是服务实例告诉 Redis 的,但多个实例的时钟是无法确保一致的,那么会出现如下情况。
假设 timePeriodInSeconds 为 5 ,allowableNumberOfCalls 为 1 ,当前时间(正确的时钟)为 2022-03-10 22:00:00 ,实例 1 的时钟跟正确的时钟一样,实例 2 的时钟比正确的时钟慢了 3 秒。
用户此时访问了资源,该请求被实例 1 处理,能获取到资源,因为 ta 之前从没访问过,所以不会被限制。过了 6 秒,ta 又访问了资源,正常的结果是用户可以访问到资源,但是结果并不是,因为这次请求被实例 2 处理,而实例 2 此时的时间为 2022-03-10 22:00:03 ,rateLimiting.lua 最后会返回 false 。
怎么解决上述问题?或者有什么更好的方法实现 rate limiting ?
1
Jooooooooo 2022-03-10 23:03:01 +08:00
需要这么精准吗?
"怎么解决上述问题?" 和 pm 沟通是不是换个需求. |
2
JasonLaw OP @Jooooooooo #1 pm 没有这个需求,是我自己实现的😅。但我不知道怎么解决时钟的问题。🤐
|
3
sujin190 2022-03-10 23:22:26 +08:00 via Android
@JasonLaw 毫秒级,秒级,分钟级,解决方案不一样啊,不要试图用一种方案适配所有场景,分钟级限制那么一两秒差异无所谓,需要秒级就需要高可用强一致的时间源,毫秒级你需要原子钟,据说谷歌为了跨地区一致性搞过这个😅😅
|
4
zeni123 2022-03-10 23:22:30 +08:00 via iPhone
告诉用户它的时钟和服务器的不同步。 即使你能解决你的服务器的时间同步不问题 你也解决不了用户和你的服务器时间不同步的问题。 所以你也不需要解决你的服务器时间同步了 直接甩锅给用户。或者换一种算法。
|
5
Jooooooooo 2022-03-10 23:27:45 +08:00
@JasonLaw 你如果要考虑这种问题的话那需要考虑的问题就多了去了.
比如各个请求阶段(请求发出没到 redis 集群, 请求发出到了 redis 机器但还没处理, 请求发出到了 redis 机器已经处理但是还没开始同步...等等)过程中 redis 机器宕机怎么办. |
6
546L5LiK6ZOt 2022-03-10 23:31:03 +08:00
不考虑使用单机限速吗,不依赖网络。如果应用实例数量不是经常变化,单机限速就够了。或者也可以考虑再加上动态配置,这样集群扩容,手动改一下限速就好。
|
7
Gota 2022-03-11 00:05:13 +08:00
可以参考这个实现, 或者直接用它的库: https://github.com/go-redis/redis_rate/blob/v9/lua.go
|
9
hankai17 2022-03-11 18:39:50 +08:00
|