Jedis与Lua脚本结合

使用Lua脚本的好处
   一、减小网络开销:能够将多个请求经过脚本的形式一次发送,减小网络时延和请求次数。

   二、原子性的操做: Redis会将整个脚本做为一个总体执行,中间不会被其余命令插入。所以在编写脚本的过程当中无需担忧会出现竞态条件,无需使用事务。

   三、代码复用:客户端发送的脚步会永久存在redis中,这样,其余客户端能够复用这一脚原本完成相同的逻辑。

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

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

    六、源码小巧: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脚本,采用的也是相似上述表达式,后面描述java

(2)对于一段长的lua脚本,能够将脚本放在一个文件中,经过以下命令执行lua脚本redis

$ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] ...apache

              --eval,告诉redis-cli读取并运行后面的lua脚本
               path/to/redis.lua,是lua脚本的位置
               KEYS[1] KEYS[2],是要操做的键,能够指定多个,在lua脚本中经过KEYS[1], KEYS[2]获取
               ARGV[1] ARGV[2],参数,在lua脚本中经过ARGV[1], ARGV[2]获取。数组

注意: KEYS和ARGV中间的 ',' 两边的空格,不能省略。浏览器

 

看下面例子:缓存

在以下文件夹中以一个lua脚本    jedisCallLuaTest.lua服务器

local key=KEYS[1local args=ARGV 
//说明:设置一个key = userName,value=Jack,20s过时时间
return redis.call("setex",key,unpack(args))

去客户端获取:网络

 

过时了。。。。ide


EVALSHA命令
将脚本 script 添加到脚本缓存中,但并不当即执行这个脚本。
语法以下:
redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...]

参数说明:函数

  • sha1 : 经过 SCRIPT LOAD 生成的 sha1 校验码。
  • numkeys: 用于指定键名参数的个数。
  • key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数能够在 Lua 中经过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
  • arg [arg ...]: 附加参数,在 Lua 中经过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量相似( ARGV[1] 、 ARGV[2] ,诸如此类)。

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[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:\\xxxx\\ideaProjects\\java8-pro\\resource\\jedisCallLuaTest.lua"; luaStr = Files.lines(Paths.get(path),Charset.defaultCharset()).collect(Collectors.joining()); System.out.println(luaStr); }

运行结果:

相关文章
相关标签/搜索