Redis Lua脚本实现ip限流示例


Posted in Redis onJuly 15, 2022

引言

分布式限流最关键的是要将限流服务做成原子化,而解决方案可以使使用redis+lua或者nginx+lua技术进行实现,通过这两种技术可以实现的高并发和高性能。
首先我们来使用redis+lua实现时间窗内某个接口的请求数限流,实现了该功能后可以改造为限流总并发/请求数和限制总资源数。Lua本身就是一种编程语言,也可以使用它实现复杂的令牌桶或漏桶算法。
如下操作因是在一个lua脚本中(相当于原子操作),又因Redis是单线程模型,因此是线程安全的。

相比Redis事务来说,Lua脚本有以下优点

减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;
原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;
复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.

Lua脚本

local key = KEYS[1] --限流KEY(一秒一个)
local limit = tonumber(ARGV[1]) --限流大小
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then --如果超出限流大小
    return 0
else --请求数+1,并设置2秒过期
    redis.call("INCRBY", key,"1")
    redis.call("expire", key,"2")
end
return 1

java代码

import org.apache.commons.io.FileUtils;
import redis.clients.jedis.Jedis;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class RedisLimitRateWithLUA {
    public static void main(String[] args) {
        final CountDownLatch latch = new CountDownLatch(1);
        for (int i = 0; i < 7; i++) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        latch.await();
                        System.out.println("请求是否被执行:"+accquire());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        latch.countDown();
    }
    public static boolean accquire() throws IOException, URISyntaxException {
        Jedis jedis = new Jedis("127.0.0.1");
        File luaFile = new File(RedisLimitRateWithLUA.class.getResource("/").toURI().getPath() + "limit.lua");
        String luaScript = FileUtils.readFileToString(luaFile);
        String key = "ip:" + System.currentTimeMillis()/1000; // 当前秒
        String limit = "5"; // 最大限制
        List<String> keys = new ArrayList<String>();
        keys.add(key);
        List<String> args = new ArrayList<String>();
        args.add(limit);
        Long result = (Long)(jedis.eval(luaScript, keys, args)); // 执行lua脚本,传入参数
        return result == 1;
    }
}

运行结果

请求是否被执行:true
请求是否被执行:true
请求是否被执行:false
请求是否被执行:true
请求是否被执行:true
请求是否被执行:true
请求是否被执行:fals

从结果可看出只有5个请求成功执行

IP限流Lua脚本

local key = "rate.limit:" .. KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = ARGV[2]
local is_exists = redis.call("EXISTS", key)
if is_exists == 1 then
    if redis.call("INCR", key) > limit then
        return 0
    else
        return 1
    end
else
    redis.call("SET", key, 1)
    redis.call("EXPIRE", key, expire_time)
    return 1
end

以上就是Redis Lua脚本实现ip限流示例的详细内容,更多关于Redis Lua限流的资料请关注三水点靠木其它相关文章!


Tags in this post...

Redis 相关文章推荐
解决redis sentinel 频繁主备切换的问题
Apr 12 Redis
redis连接被拒绝的解决方案
Apr 12 Redis
分布式锁为什么要选择Zookeeper而不是Redis?看完这篇你就明白了
May 21 Redis
redis客户端实现高可用读写分离的方式详解
Jul 04 Redis
Redis的字符串是如何实现的
Oct 24 Redis
Springboot/Springcloud项目集成redis进行存取的过程解析
Dec 04 Redis
Redis之RedisTemplate配置方式(序列和反序列化)
Mar 13 Redis
Redis如何使用乐观锁(CAS)保证数据一致性
Mar 25 Redis
 Redis 串行生成顺序编码的方法实现
Apr 03 Redis
Redis 报错 error:NOAUTH Authentication required
May 15 Redis
Redis keys命令的具体使用
Jun 05 Redis
Redis批量生成数据的实现
Jun 05 Redis
redis protocol通信协议及使用详解
Jul 15 #Redis
Redis sentinel哨兵集群的实现步骤
Jul 15 #Redis
Redis唯一ID生成器的实现
Jul 07 #Redis
Redis+AOP+自定义注解实现限流
Jun 28 #Redis
利用Redis实现点赞功能的示例代码
Jun 28 #Redis
一文教你快速生成MySQL数据库关系图
Jun 28 #Redis
Redis实现主从复制方式(Master&Slave)
Jun 21 #Redis
You might like
2019年漫画销量排行榜:鬼灭登顶 海贼单卷制霸 尾田盛赞鬼灭
2020/03/08 日漫
基于PHP与XML的PDF文档生成技术
2006/10/09 PHP
php读取excel文件示例分享(更新修改excel)
2014/02/27 PHP
php的declare控制符和ticks教程(附示例)
2014/03/21 PHP
php中get_cfg_var()和ini_get()的用法及区别
2015/03/04 PHP
ThinkPHP自定义函数解决模板标签加减运算的方法
2015/07/03 PHP
PHPCMS2008广告模板SQL注入漏洞修复
2016/10/11 PHP
Laravel框架实现利用中间件进行操作日志记录功能
2018/06/06 PHP
laravel config文件配置全局变量的例子
2019/10/13 PHP
extJS中常用的4种Ajax异步提交方式
2014/03/07 Javascript
javascript精确统计网站访问量实例代码
2015/12/19 Javascript
jquery实现全选功能效果的实现代码
2016/05/05 Javascript
深入理解JavaScript内置函数
2016/06/03 Javascript
JS遍历页面所有对象属性及实现方法
2016/08/01 Javascript
js实现增加数字显示的环形进度条效果
2017/02/05 Javascript
基于Datatables跳转到指定页的简单实例
2017/11/09 Javascript
Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)
2020/03/06 Javascript
原生js canvas实现鼠标跟随效果
2020/08/02 Javascript
vue+echarts实现动态折线图的方法与注意
2020/09/01 Javascript
解决Vue-cli无法编译es6的问题
2020/10/30 Javascript
Vue实现指令式动态追加小球动画组件的步骤
2020/12/18 Vue.js
Vite和Vue CLI的优劣
2021/01/30 Vue.js
[01:46]新英雄登场
2019/09/10 DOTA
python同时给两个收件人发送邮件的方法
2015/04/30 Python
Python Tkinter实现简易计算器功能
2018/01/30 Python
Python代码需要缩进吗
2020/07/01 Python
会走动的图形html5时钟示例
2014/04/27 HTML / CSS
Urban Outfitters美国官网:美国生活方式品牌
2016/08/26 全球购物
施华洛世奇水晶荷兰官方网站:SWAROVSKI荷兰
2017/05/12 全球购物
夜大自我鉴定
2013/10/31 职场文书
求职信名称怎么写
2014/05/26 职场文书
本科应届生求职信
2014/08/05 职场文书
服务员岗位职责
2015/02/03 职场文书
php中pcntl_fork详解
2021/04/01 PHP
Python 中数组和数字相乘时的注意事项说明
2021/05/10 Python
Python中22个万用公式的小结
2021/07/21 Python