redis+lua实现简单的分布式接口限流器

本文介绍了一个简单的分布式接口限流器,使用Redis和Lua脚本实现。该方案利用Redis的List数据结构,通过lua脚本确保操作的原子性,实现基于时间滑动窗口的限流策略。当请求超过预设阈值时,会阻止新的请求,直到满足限流条件。此外,提供了SpringBoot中配置Lua脚本和Java服务方法的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

轻量级分布式接口限流器

已发布github:https://github.com/z-meng/CurrentRateLimiter

相对于令牌桶,该方案思路、设计都简单易上手
算法设计:基于redis + lua
优点:简单,具有基于时间的滑动窗口功能
缺点:当前没有阻塞策略
整体思路,
1、每次请求操作redis链表,查询存在不存在,不存在则新增基于条件(用户+接口/IP+接口)的链表,value为当前接口请求的时间戳,
2、当表存在时候,判断长度是否小于限流阈值,不小于则从左边插入当前时间戳
3、否则的话就从右边弹出一个数据,判断该数据和当前接口请求的时间戳的差值,是否大于 1S,大于,说明处于阈值的时间戳和当前请求的时间差超过了1S,那么起码可以请求一次,满足当前请求,把当前请求插入,并截取链表长度,到阈值长度(其实没必要截取了,从右边弹出 + lua原子性 + redis单线程,此时已经保证了链表中最多只有=阈值的数据,因为每当该数据等于阈值时候,就把最早的数据弹出了)

	local key = KEYS[1]
local nowMill = tonumber(ARGV[1]) --当前时间戳毫秒
local limit = tonumber(ARGV[2]) --限流次数
local interval = tonumber(ARGV[3]) --限流间隔 单位是毫秒
local startIndex = 0
local endIndex = limit -1

--1、判断是否存在key
local isExists = redis.call("EXISTS",key);
if not isExists then
    redis.call("LPUSH",key,nowMill) --不存在 插入
else
    local size = tonumber(redis.call("LLEN",key)) --获取列表长度
    if size >= limit then
        local lastMill = tonumber(redis.call("RPOP",key)) --获取最后一个数据
        local diffMill = nowMill - lastMill
        if diffMill <= interval then
            redis.call("LPUSH",key,nowMill) --从头部插入数据
            redis.call("LTRIM",key,startIndex,endIndex) --剪切保留最新的limit条记录
            return false
        end
    end
    redis.call("LPUSH",key,nowMill) --从头部插入数据
end
return true

lua基于springboot的配置

	@Service
public class LuaConfig {
    @Bean
    public DefaultRedisScript<Boolean> currentLimitScript() {
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/currentlimit.lua")));
        redisScript.setResultType(Boolean.class);
        return redisScript;
    }
}

java的service方法

String key = PREFIX + userId;
        boolean flag = redisTemplate.execute(currentLimitScript, Collections.singletonList(key), Long.toString(System.currentTimeMillis()),
                Integer.toString(limit), Long.toString(interval));
        if(!flag){
            log.error("接口调用过于频繁!");
        }
        return flag;

到此就结束了,每次接口调用的入口就调用该方法,就能达到分布式限流的功能,lua原子性、redis单线程、也可以写一个自定义注解,这里比较懒,没有实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值