利用 Redis 和 Lua 实现高效的限流功能

简介

在现代系统中,限流是一种重要的机制,用于控制服务端的流量并保护系统免受恶意攻击或请求泛滥的影响。本文将介绍如何利用 Redis 和 Lua 结合实现高效的限流功能。

一、什么是限流

限流指的是对系统中的请求进行控制和调节,确保系统在承受压力时能够正常运行,不会因为突然的大量请求导致系统宕机或服务质量下降。限流在系统中具有至关重要的作用,可以平稳地处理请求流量,防止系统过载。

二、什么是Redis

Redis是一个高性能的内存数据库,具有快速的读写速度和丰富的数据结构支持。在限流场景中,Redis可以作为一个高效的缓存和计数工具,帮助实现限流功能。

三、什么是lua

Lua是一种轻量级的脚本语言,它具有简洁的语法和高效的执行性能。

四、限流要用lua+Redis而不用Java、Python 等语言呢?

  • 性能和原子性: Lua脚本可以在Redis服务器端原子性地执行多个命令,避免了多次网络通信的开销,提高了性能和原子性。相比之下,用Java或Python实现的限流算法需要多次与Redis进行通信,性能相对较低。

  • 便捷性: Lua脚本可以直接在Redis服务器端执行,无需额外部署其他语言的运行环境,更加灵活和便捷。

  • Redis支持: Redis天然支持Lua脚本,可以直接执行,不需要额外的配置和插件。而如果使用Java或Python,需要额外的库或框架来与Redis进行交互。

五、限流算法选择

5.1 令牌桶算法

令牌桶算法中,存在一个令牌桶,可以往桶中添加令牌。每个令牌代表一个处理请求的许可。如果请求到了,桶中有足够的令牌,择允许处理该请求,同时消耗相应数量的令牌。如果桶中没有足够的令牌,则拒接该请求或将请求放入队列等待令牌。

5.2 漏桶算法

漏桶算法中,存在一个固定容量的漏桶,以固定的速率处理请求。如果请求到来,放入漏桶中,如果漏桶已满,则拒绝请求,如果漏桶未满,则按照固定的速率处理请求。

六、lua+Redis实现令牌桶算法

Lua 复制代码
local key = KEYS[1] -- 获取传入lua脚本的第一个keys参数,用作存储令牌数目的键名
local limit = tonumber(ARGV[1]) -- 将传入lua脚本的第一个ARGV参数转换为整数,表示桶的容量
local current = tonumber(redis.call('get', key) or "0")
-- 通过Redis的GET命令获取当前令牌桶中的令牌数量,如果没有获取到则默认为0,并将其转换为整数。
​
if current + 1 > limit then -- 判断当前令牌桶中的令牌数量加1后是否超过阈值
    return 0 -- 超过表示请求被限流,返回0
else
    redis.call('INCR', key) -- 通过Redis的INCR命令将令牌桶中的数量加1,表示通过了一个请求
    redis.call('EXPIRE', key, ARGV[2]) -- 设置令牌桶的过期时间为ARGV 参数中指定的时间
 
    return 1  -- 返回1,表示通过限流检查
end

七、lua+Redis实现漏桶算法

Lua 复制代码
local key = KEYS[1] -- 限流器的键名
local capacity = tonumber(ARGV[1]) -- 漏桶的容量
local rate = tonumber(ARGV[2]) -- 漏桶的速率
local now = tonumber(ARGV[3]) -- 当前时间戳
local interval = 1 / rate -- 时间间隔,即每个请求需要等待的时间
​
local water = tonumber(redis.call('get', key) or "0") -- 获取漏桶中的水滴数量
local lastLeakTime = tonumber(redis.call('get', key .. ':last_leak_time') or "0") -- 上次漏水的时间戳
​
local elapsed = math.max(0, now - lastLeakTime) -- 计算当前时间与上次漏水的时间间隔
​
water = water - elapsed * rate -- 根据时间间隔计算漏水数量,并更新漏桶中的水滴数量
​
if water < 0 then
    water = 0 -- 水滴数量不会低于0
end
​
water = water + 1 -- 新的请求加入漏桶中
​
if water > capacity then
    return 0 -- 漏桶已满,拒绝请求
else
    redis.call('set', key, water) -- 更新漏桶中的水滴数量
    redis.call('set', key .. ':last_leak_time', now) -- 更新上次漏水的时间戳
    return interval -- 返回请求需要等待的时间间隔
end
相关推荐
敲个大西瓜5 小时前
Redis一百道核心面试题
数据库·redis·缓存
無a伟8 小时前
Redis持久化详解:RDB与AOF原理、配置、优缺点
数据库
小罗水9 小时前
附录A 各微服务完整 application.yml 配置汇总
数据库·elasticsearch·微服务
muddjsv9 小时前
SQLite 外键进阶:CASCADE / SET NULL / 级联更新与完整性校验
数据库·sqlite
APItesterCris9 小时前
Open Claw 实战教程:5 分钟搭建京东商品自动化监控与数据分析系统
大数据·运维·数据库·数据仓库·自动化
qq_370773099 小时前
Unity游戏xLua逆向实战:从SO版本识别到Lua脚本热替换全链路
游戏·unity·lua
Nturmoils10 小时前
查库存的 SQL 时灵时不灵,最后发现是 WHERE 里两个函数在打架
数据库
不想纳尼的青春10 小时前
where = 的作用?会影响性能吗?count(*) 和 count()哪个快?
数据库·oracle
倔强的石头_11 小时前
Spring Boot 接入金仓数据库:配置分层、启动自检与常见错误
数据库
杨云龙UP11 小时前
生产环境Oracle表空间扩容实战:使用Toad for Oracle图形界面新增Datafile(ASM+OMF)
linux·运维·数据库·oracle·表空间·asm 存储管理·toad