深入理解redis中的lua脚本

 2023-09-15 阅读 18 评论 0

摘要:本文来说下redis中的lua脚本 redis脚本、 文章目录概述Lua简介使用Lua脚本的好处Redis+Lua实现限流本文小结 概述 今天讲一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当中,来扩展其

本文来说下redis中的lua脚本

redis脚本、

文章目录

  • 概述
  • Lua简介
  • 使用Lua脚本的好处
  • Redis+Lua实现限流
  • 本文小结


概述

今天讲一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当中,来扩展其功能。lua脚本是用C语言写的,体积很小,运行速度很快,并且每次的执行都是作为一个原子事务来执行的,我们可以在其中做很多的事情

在这里插入图片描述


Lua简介

Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。

Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,ini等文件格式,并且更容易理解和维护。Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。一个完整的Lua解释器不过200k,在目前所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式脚本的最佳选择


使用Lua脚本的好处

1、减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数。

2、原子性的操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。

3、代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑。

4、速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处。

5、可以移植:只要是有ANSI C 编译器的平台都可以编译,你可以看到它可以在几乎所有的平台上运行:从 Windows 到Linux,同样Mac平台也没问题, 再到移动平台、游戏主机,甚至浏览器也可以完美使用 (翻译成JavaScript).

6、源码小巧:20000行C代码,可以编译进182K的可执行文件,加载快,运行快。


Redis+Lua实现限流

分布式限流最关键的是要将限流服务做成原子化,而解决方案可以使使用redis+lua或者nginx+lua技术进行实现,通过这两种技术可以实现的高并发和高性能。

首先我们来使用redis+lua实现时间窗内某个接口的请求数限流,实现了该功能后可以改造为限流总并发/请求数和限制总资源数。Lua本身就是一种编程语言,也可以使用它实现复杂的令牌桶或漏桶算法

如下操作因是在一个lua脚本中(相当于原子操作),又因Redis是单线程模型,因此是线程安全的。

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

减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;

原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;

复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.

Lua脚本

local key = KEYS[1] --限流KEY
local capacity = tonumber(ARGV[1]) --限流大小
local duration = ARGV[2] --过期时间,秒local current = tonumber(redis.call('INCR', key))
if current > capacity then --如果超出限流大小return 0
elseif current == 1thenredis.call("EXPIRE", key, duration)endreturn tonumber(redis.call("GET", key))
end

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
请求是否被执行:false

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 thenif redis.call("INCR", key) > limit thenreturn 0elsereturn 1end
elseredis.call("SET", key, 1)redis.call("EXPIRE", key, expire_time)return 1
end

本文小结

本文详细介绍了redis中lua脚本相关的知识与内容。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/62524.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息