Jedis与Lua脚本结合
使用Lua脚本的好处
? ?1、减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数。
? ?2、原子性的操作: Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。 ? ?3、代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑。 ? ?4、速度快:见 与其它语言的性能比较,还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人,可以把关键部分使用C实现,然后与其集成,这样还可以享受其它方面的好处。 ? ? 5、可以移植:只要是有ANSI C 编译器的平台都可以编译,你可以看到它可以在几乎所有的平台上运行:从 Windows 到Linux,同样Mac平台也没问题,再到移动平台、游戏主机,甚至浏览器也可以完美使用 (翻译成JavaScript). ? ? 6、源码小巧:20000行C代码,可以编译进182K的可执行文件,加载快,运行快。
?
eval 命令
(1)首先需要了解Redis eval 命令:
eval 命令也会将脚本添加到脚本缓存中,但是它会立即对输入的脚本进行求值。
?
eg
EVAL script numkeys key [key ...] arg [arg ...]
script参数是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数。
numkeys参数用于指定键名参数的个数。
键名参数 key [key ...] 从EVAL的第三个参数开始算起,表示在脚本中所用到的那些Redis键(key),这些键名参数可以在 Lua中通过全局变量KEYS数组,用1为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
在命令的最后,那些不是键名参数的附加参数 arg [arg ...] ,可以在Lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)
?
如:
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 username age jack 20
一般java运行lua脚本,采用的也是类似上述表达式,后面描述 (2)对于一段长的lua脚本,可以将脚本放在一个文件中,通过如下命令执行lua脚本 $ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2],ARGV[1] ARGV[2] ... ????????? ? ? --eval,告诉redis-cli读取并运行后面的lua脚本 注意: KEYS和ARGV中间的 ‘,‘ 两边的空格,不能省略。 ? 看下面例子: 在如下文件夹中以一个lua脚本 ? ?jedisCallLuaTest.lua local key=KEYS[1]? local args=ARGV? 去客户端获取:
?
过期了。。。。 EVALSHA命令 将脚本?script?添加到脚本缓存中,但并不立即执行这个脚本。
语法如下:
redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...]
参数说明:
eg: 整合Jedis + lua (1)Jedis的使用
创建Jedis对象,主要是用于单个redis
?
public?class?RedisClient?{ ????private?static?JedisPool????jedisPool????=?null; ????private?static?String????????addr????????=?"127.0.0.1"; ????private?static?int????????????port????????=?6379; ????static?{ ????????try?{ ????????????JedisPoolConfig?config?=?new?JedisPoolConfig(); ????????????//?连接耗尽时是否阻塞,?false报异常,ture阻塞直到超时,?默认true ????????????config.setBlockWhenExhausted(true); ????????????//?设置的逐出策略类名,?默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最?大空闲连接数) ????????????config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy"); ????????????//?是否启用pool的jmx管理功能,?默认true ????????????config.setJmxEnabled(true); ????????????//?MBean?ObjectName?=?new ????????????//?ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" ????????????//?+?"pool"?+?i);?默认为"pool",?JMX不熟,具体不知道是干啥的...默认就好. ????????????config.setJmxNamePrefix("pool"); ????????????//?是否启用后进先出,?默认true ????????????config.setLifo(true); ????????????//?最大空闲连接数,?默认8个? ????????????config.setMaxIdle(8); ????????????//?最大连接数,?默认8个 ????????????config.setMaxTotal(8); ????????????//?获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常,?小于零:阻塞不确定的时间,????????????//?默认-1 ????????????config.setMaxWaitMillis(-1); ????????????//?逐出连接的最小空闲时间?默认1800000毫秒(30分钟) ????????????config.setMinEvictableIdleTimeMillis(1800000); ????????????//?最小空闲连接数,?默认0 ????????????config.setMinIdle(0); ????????????//?每次逐出检查时?逐出的最大数目?如果为负数就是?:?1/abs(n),?默认3 ????????????config.setNumTestsPerEvictionRun(3); ????????????//?对象空闲多久后逐出,?当空闲时间>该值?且?空闲连接>最大空闲数 ????????????//?时直接逐出,不再根据MinEvictableIdleTimeMillis判断?(默认逐出策略) ????????????config.setSoftMinEvictableIdleTimeMillis(1800000); ????????????//?在获取连接的时候检查有效性,?默认false ????????????config.setTestOnBorrow(false); ????????????//?在空闲时检查有效性,?默认false ????????????config.setTestWhileIdle(false); ????????????//?逐出扫描的时间间隔(毫秒)?如果为负数,则不运行逐出线程,?默认-1 ????????????config.setTimeBetweenEvictionRunsMillis(-1); ????????????jedisPool?=?new?JedisPool(config,?addr,?port,?3000); ????????}?catch?(Exception?e)?{ ????????????e.printStackTrace(); ????????} ????} ???? ????public?synchronized?static?Jedis?getJedis()?{ ????????try?{ ????????????if?(jedisPool?!=?null)?{ ????????????????Jedis?resource?=?jedisPool.getResource(); ????????????????return?resource; ????????????}?else?{ ????????????????return?null; ????????????} ????????}?catch?(Exception?e)?{ ????????????e.printStackTrace(); ????????????return?null; ????????} ????} ????public?static?void?close(final?Jedis?jedis)?{ ????????if?(jedis?!=?null)?{ ????????????jedis.close(); ????????} ????} ???? ????/*---------------------测试---------------------------*/ ????public?static?void?main(java.lang.String[]?args)?{ ????????Jedis?jedis?=?RedisClient.getJedis(); ????????//?do?something ????????RedisClient.testCallLua(jedis); ????????RedisClient.close(jedis); ????} ???? ????public?static?void?testCallLua(Jedis?jedis){ ????????String?luaStr?=?"return?{KEYS[1],KEYS[1],ARGV[2]}"; ????????Object?result?=?jedis.eval(luaStr,?Lists.newArrayList("userName","age"),?Lists.newArrayList("Jack","20")); ????????System.out.println(result); ????} } 运行结果: [userName,?userName,?Jack,?20]
同理将工程中的lua文件加载成String,作为参数运行也是可行的。
java: 该段代码图个方便,引用了guava.jar包 /** ?????*?调用lua脚本 ?????*?@param?jedis ?????*/ ????public?static?void?testCallLuaFile(Jedis?jedis){ ????????String?luaStr?=?null; ???????? ????????//带反斜杠,路径为classPath,不带反斜杠,路径为类的同一目录 ????????Reader?r?=?new?InputStreamReader(RedisClient.class.getResourceAsStream("/jedisCallLuaTest.lua")); ????????try?{ ????????????luaStr?=?CharStreams.toString(r); ????????????Object?result?=?jedis.eval(luaStr,?Lists.newArrayList("userName"),?Lists.newArrayList("20","Tom")); ????????????System.out.println(result); ????????}?catch?(IOException?e)?{ ????????????e.printStackTrace(); ????????} ????} 注意:getResourceAsStream()这可是好帮手,用到将文件内容加载成String,一定要想到他。CharStreams是guava.jar中的对象。 Reader转String还有如下两种伎俩:(发散。。。。) @Test public void apcheIo() throws IOException{ String luaStr=null; //带反斜杠,路径为classPath,不带反斜杠,路径为类的同一目录 Reader r= new InputStreamReader(Reader2StrDemo.class.getResourceAsStream("/jedisCallLuaTest.lua")); luaStr = org.apache.commons.io.IOUtils.toString(r); System.out.println(luaStr); } @Test public void java8() throws IOException{ String luaStr=null; String path = "F:xxxxideaProjectsjava8-proresourcejedisCallLuaTest.lua"; luaStr = Files.lines(Paths.get(path),Charset.defaultCharset()).collect(Collectors.joining()); System.out.println(luaStr); } 运行结果: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |