面试题答案
一键面试令牌桶算法基本原理
- 令牌生成:系统以固定速率生成令牌,并放入令牌桶中。例如,每100毫秒生成1个令牌。
- 令牌存储:令牌桶有一个固定容量,当令牌桶满时,新生成的令牌会被丢弃。假设令牌桶容量为100个令牌。
- 请求处理:当有请求(如消息发送请求)到来时,尝试从令牌桶中获取令牌。如果桶中有足够令牌,则请求可以通过并执行相应操作(如发送消息),同时从桶中移除相应数量的令牌;如果桶中令牌不足,则请求被限流,通常需要等待或直接拒绝。
在Redis中模拟实现令牌桶算法控制消息发送速率
- 数据结构选择:
- 使用Redis的
zset
(有序集合)来记录每次生成令牌的时间戳以及对应的令牌数量。zset
的成员可以是时间戳,分值可以是生成的令牌数。 - 使用Redis的
string
类型记录当前令牌桶中的令牌总数。
- 使用Redis的
- 实现步骤:
- 生成令牌:
- 定期任务(如通过定时脚本)按照固定速率生成令牌。每次生成令牌时,获取当前时间戳
now
,将其作为zset
的成员,生成的令牌数量作为分值,添加到zset
中。例如,使用ZADD <zset_key> <score> <member>
命令,score
为生成的令牌数,member
为now
。 - 同时,将新生成的令牌数累加到记录当前令牌总数的
string
类型键的值上,使用INCRBY <total_tokens_key> <new_tokens>
命令。
- 定期任务(如通过定时脚本)按照固定速率生成令牌。每次生成令牌时,获取当前时间戳
- 消息发送时获取令牌:
- 先计算从上次生成令牌到当前时间新生成的令牌数量。通过
ZRANGEBYSCORE <zset_key> -inf <now>
获取从开始到当前时间所有生成令牌的记录,计算这些记录的分值总和,得到这段时间新生成的令牌数。 - 用当前令牌总数加上新生成的令牌数,更新当前令牌总数记录(
string
类型键的值)。 - 当有消息发送请求时,检查当前令牌总数是否足够。如果足够,从当前令牌总数中减去发送消息所需的令牌数(如1个令牌),使用
DECRBY <total_tokens_key> <tokens_needed>
,然后允许消息发送;如果不足,则拒绝消息发送。
- 先计算从上次生成令牌到当前时间新生成的令牌数量。通过
- 生成令牌: