java操作lua脚本

java操作lua脚本实例

  1. 前言 在上一篇文章 Redis中使用Lua脚本来实现并发下的原子操作 中我对Lua语言的一些简单的语法及其在Redis中的操作进行了介绍,但是在Java开发中我们还需要进一步的学习才能使这种技术落地。今天就结合Spring Data Redis这个我们经常使用的Redis开发组件来实际尝试一下Lua 脚本。

  2. Lua 实现抽奖 模拟一个抽奖场景,从奖池中进行随机抽奖。规则如下:

中奖的人只能从奖池中抽取。 每个人只能中奖一次。 中奖总人数不能超过奖项的设置数。 生成中奖名单。 规则有了,我们先来分析如何使用Redis实现。Redis提供了SET集合,这种集合有点类似Java中的Set,放无重复的元素而且是无序的,可以满足随机性和奖池候选人的唯一性。同时它还提供了很多操作来满足抽奖的需要。接下来我们进行一一演示。

Redis SET 的一些操作。 基于篇幅我这里只演示一些抽奖可以用的上的Redis操作。

SET添加元素。 添加一个到多个元素,使用SADD命令往lottery中添加多个元素来模拟往奖池中加人。

127.0.0.1:6379> sadd lottery u1 u2 u3 u4 u5 u6 u7 (integer) 7 127.0.0.1:6379> sadd lottery u1 (integer) 0

如果没有lottey这个key就新建该key,有就直接添加并返回成功添加的元素个数。同时你会发现如果集合中存在了添加的元素是无法被再次添加的。

查询集合中的元素 查询所有元素通过SMEMBERS命令。

127.0.0.1:6379> smembers lottery 1) "u2" 2) "u7" 3) "u6" 4) "u4" 5) "u1" 6) "u3" 7) "u5"

随机抽取N个元素 SET集合有两个命令都能满足随机抽取N个元素,分别是SPOP和SRANDMEMBER,它们的区别在于SPOP会将选中的元素从原来的集合中剔除,而SRANDMEMBER不会。我们分别来使用这两个命令来随机从lottery中抽取2个元素来看看。

127.0.0.1:6379> srandmember lottery 2 1) "u2" 2) "u4" 127.0.0.1:6379> smembers lottery 1) "u2" 2) "u7" 3) "u6" 4) "u4" 5) "u1" 6) "u3" 7) "u5" 127.0.0.1:6379> spop lottery 2 1) "u3" 2) "u5" 127.0.0.1:6379> smembers lottery 1) "u2" 2) "u7" 3) "u6" 4) "u4" 5) "u1"

就lottery来说,如果你的奖池人数一次性添加的不再增加使用SPOP;如果动态添加,为了保证中奖的人不再次进入奖池应该使用SRANDMEMBER。

抽奖脚本 接下来就是抽奖脚本,我们从lottery中抽出特定的人放入中奖名单,另外一个集合chosen中。

按道理Redis抽奖脚本在Lua中应该是这样的:

function draw(KEYS,ARGV) -- 抽奖逻辑 函数体 end

但是我们只需要编写抽奖逻辑的函数体,然后把函数体写入.lua文件中,在Maven项目中放入META-INF/scripts文件夹中,如图所示:

Redis结合Lua脚本实现抽奖逻辑 约定lua脚本所在的目录

draw.lua的逻辑为:

--- 简单抽奖脚本 return 结果最终传递给Java 应用 -- 奖池的key local lottery_key = KEYS[1] -- 中奖名单的key local chosen_key = KEYS[2] -- 预定抽奖的人数 local lottery_count = ARGV[1] -- 如果预定抽奖的人数大于0才开始抽奖 if tonumber(lottery_count) > 0 then -- 奖池中抽奖 返回的是 被抽中的人组成的数组 local chosen_list = redis.call('SRANDMEMBER', lottery_key, lottery_count); -- 将抽中的人添加到中奖名单中 返回中奖的人数 if chosen_list then return redis.call('SADD', chosen_key, unpack(chosen_list)) else return 0 end else return 0 end

  1. 对应的 Java 代码 Spring Data Redis中的RedisTemplate提供了execute方法来执行Lua脚本,这里我选择使用下面的方法:

    @Override public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) { return scriptExecutor.execute(script, keys, args); }

RedisScript Redis脚本的抽象,用来加载脚本。 keys对应Lua脚本中的KEYS,用来传入Redis的KEY,在Lua脚本中可以通过 KEYS[索引]来取值,例如取第一个值KEYS[1]。 args用来向Lua脚本传递其它的参数,在Lua脚本中可以通过ARGV[索引]来取值。 我们利用draw.lua脚本从Redis的lottery集合中抽取5名幸运者并把他们添加到中奖名单chosen集合中:

RedisScript<Long> redisScript = RedisScript.of(new ClassPathResource("META-INF/scripts/draw.lua"), Long.class); Long chosenCount = stringRedisTemplate.execute(redisScript, Arrays.asList("lottery", "chosen"), Collections.singletonList("5")); 构造RedisScript对象时务必指定返回值对象以保证Lua脚本对象和Java的返回值能对应上, 否则将出现异常。参见org.springframework.data.redis.connection.ReturnType枚举。

  1. 总结 到此Redis利用Lua脚本进行抽奖的整套逻辑就完成了。Lua脚本在Redis中通常是为了保证高并发下的原子性,当你考虑是否需要使用它时应该充分考虑你的业务和架构是否适合使用它。
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Redis+Lua——他叫了外援

    Redis从2.6版本开始引入对Lua脚本的支持,通过在Redis服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本,直接在服务端原子的执行多个Redis命令。Lua    Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。为

Redis 脚本

Redis脚本使用Lua解释器来执行脚本。Reids2.6版本通过内嵌支持Lua环境。执行脚本的常用命令为EVAL。语法Eval命令的基本语法如下:redis127.0.0.1:6379EVALscriptnumkeyskeykey...argarg...实例

java操作lua脚本 - HelloWorld