我们在开发接口服务器的过程中,为了防止客户端对于接口的滥用,保护服务器的资源, 通常来说我们
会对于服务器上的各种接口进行调用次数的限制。
比如对于某个 用户,他在一个时间段(interval)内,比如 1 分钟,调用服务器接口的次数不能够大于
一个上限(limit),比如说 100 次。如果用户调用接口的次数超过上限的话,就直接拒绝用户的请求,
返回错误信息。
最初的想法
对于实现方法的第一印象,大概是,给每个用户一个配额,次数为 Q, 这个配额在用户第一次调用接口
的时候分配给用户。然后在接下去的 P 时间段内,如果用户访问 API 的次数大于 Q,就开始拒绝用户的
调用请求。然后,这个 配额,在 P 时间之后,配额会被重置回 Q。
逻辑的伪代码见如下:
can_access(identity):
limit_counter = get(identity)
if limiter_counter exists and
limiter_counter.timestamp - CURRENT_TIME < LIMIT_INTERVAL and
limit_counter >= limit:
return false
else
if limiter_counter is nil or
(limit_counter exists and limiter_counter.timestamp - CURRENT_TIME > LIMT_INTERVAL):
put(identity, new LimiterCounter(1, CURRENT_TIME))
else
put(identity, limiter_counter.increment())
end
return true
end
Redis 的 INCR 命令可以比较简单地实现这种方法,在 INCR 的文档页下面介绍了如何使用 INCR 命令
实现 Rate Limtier。
这种实现方法,仔细想来存在一个缺陷,就是用户可以在一个时间段的末尾发起 Q 次请求,然后在下一
个时间段的开始又 发起 Q 次请求,这样,一个用户可以在很短的时间之内发起 2Q 次请求。
可能普通用户不会刻意这么去做,但如果真的出现这种情况的时候,服务器会承受正常情况下两倍的负
载, 这并不是我们所希望看见的。而且如果服务器被攻击的话,这种的缺陷,还是很可能会被利用的。
Token Bucket 算法
作为一个程序员,当我对于一个问题没有头绪时,可以这样:
评论2