1. 什么是限流算法
在高并发的应?程序中,限流是很重要的?个环节。限流是指对于并发访问系统的请求,对请求的流量进行限制。这是保护系统的关键所在。因为过多的请求会导致系统瘫痪,影响系统的正常运行。限流算法?被称为流量整形算法,根据算法的不同实现机制可以分为几种不同的算法,如:固定时间窗口、滑动时间窗口、令牌桶等算法。这几种算法都是官?推荐的算法中的?员。不过,本篇文章,我们只介绍Redis中最常?的流量整形算法——令牌桶。
2. 什么是令牌桶
令牌桶是指以固定速率产生令牌,每个令牌代表着一个可以通过系统的请求。如果令牌被拿完了,那么后来的请求将会被系统阻塞,直到新的令牌生成为止。这是一个队列思想的体现。所以可以定义一个令牌桶的数据结构,来保存所需要的令牌信息。个人理解它就像是提前为每一个请求进行了排队,保证了每个请求都能够得到处理。
3. Redis如何实现令牌桶
3.1 Redis中使用限流
在Redis中,我们可以通过setnx
命令实现获取锁的过程来限制请求达到并发的目的。但是限制的是整个系统的请求流量,难以针对不同的请求进行限流。所以,令牌桶的流量控制方式就应运而生了,因为令牌桶能够满足对不同请求的限流。
3.2 令牌桶实现
Redis中,我们可以通过ZSET类型来保存令牌桶的数据。ZSET类型是Redis中的一种有序集合类型,有序集合按照分数进行排序。所以,这里我们使用当前时间戳来表示每一个令牌,每次我们需要获取令牌的时候,就在ZSET中查找比当前时间戳小的最大的可用令牌,如果找到了,就返回这个令牌,并将其从ZSET类型中删除。如果没有找到可用的令牌,那么就直接返回失败。
按照上述思路,我们可以撰写脚本实现整个 Redis 令牌桶的实现。
// key为令牌桶的名称,limit为限制的速率,interval为锁的过期时间,requestSize为请求的大小
function acquire(tokens, interval, requestSize, currentTime) {
let timestamp = currentTime || Date.now();
let key = `tokens-${timestamp}`;
let limit = tokens;
let intervalTime = Math.ceil(interval / 1000);
// 创建Lua脚本,使用时间戳作为分数来进行排序(因为ZSET是有序集合),将新的令牌加入到 `` 集合里,然后获取当前 `score` 时间戳后面的第一个位置的元素。如果能获取到该元素,那么就说明在 `interval` 时间内令牌的都不为空。如果此时该元素的 `score` 到当前的秒数已经小于等于 `intervalTime`,这时可以将该计数器 `score` -1,也就是将该计数器对应的令牌-1。如果小于等于0则直接删除该计数器,即如果时间戳比第一个计数器的 `score` 所对应的时间还要新,那么可以认为其他时间戳对应的计数器都已经删除。如果没有该元素的话,那么说明 `interval` 时间前没有数据需要处理,因为通过 `timestamp` 计算的时间戳对应的 ZSET 数据集合清空了 查看是否可以获取到第一个可以操作的计数器
let keys = [key];
let args = [limit, intervalTime, requestSize, timestamp];
let script = `
local tokens_key = KEYS[1]
local limit = tonumber(ARGV[1])
local interval_time = tonumber(ARGV[2])
local request_size = tonumber(ARGV[3])
local timestamp = tonumber(ARGV[4])
redis.call('ZREMRANGEBYSCORE', tokens_key, '-inf', '(' .. (timestamp - interval_time) * limit * 1000)
local size = redis.call('ZCARD', tokens_key)
if size >= limit then
return 0
end
local total = size + request_size
if total > limit then
return 0
else
redis.call('ZADD', tokens_key, timestamp, timestamp)
return 1
end
`
let result = redis.eval(script, keys, args);
if (result === 1) { // 获取令牌成功
return true;
}
return false;
}
4. Redis中限流算法的应用
Redis中限流算法可以应用于防止雪崩、降级、限制瞬时请求率等方面。我们可以根据具体业务的需要来进行灵活使用。
其中,对于上文中提到的防止雪崩的方法,精髓之处在于合理的设置锁的过期时间,让系统中的请求不会全部在同一时刻涌入;对于降级,只要将不重要的服务进行优先级的处理就可以实现,例如将某些请求的令牌放置在 ZSET 的较后面的位置等。
5. 总结
本文主要对Redis中限流算法的核心——令牌桶进行了详细的介绍与解析,最后给出了一份完整的演示代码。限流是一个保障系统正常运行的重要环节,令牌桶是其中常用的限流算法之一,因为它能够灵活控制不同请求的流量限制。希望本文能够帮助到大家,也希望大家使用好限流算法,保护好系统稳定运行。