先写lua使用好处

  1. 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延。
  2. 原子操作。Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。
  3. 复用。客户端发送的脚本会永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。

基本使用

1、引入jar包配置序列化(这是随便在网上找的配置

@Configuration
@EnableCaching //引入缓存
public class RedisConfig {

    /**
     * RedisTemplate配置
     * @param redisConnectionFactory
     * @return
     */
    @Primary
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 设置序列化
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
                Object.class);
        ObjectMapper om = new ObjectMapper()
                .registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule()); // new module, NOT JSR310Module;;
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        // key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

2、写lua脚本

常见命令,这里就不一一介绍

 当 Lua 通过 call() 或 pcall() 函数执行 Redis 命令的时候,命令的返回值会被转换成 Lua 数据结构。同样地,当 Lua 脚本在 Redis 内置的解释器里运行时,Lua 脚本的返回值也会被转换成 Redis 协议(protocol),然后由 EVAL将值返回给客户端。redis.call() 和 redis.pcall() 的唯一区别在于它们对错误处理的不同。

redis客户端使用lua案例,数字 2 指定了键名参数的数量, key1 和 key2 是键名参数,分别使用 KEYS[1] 和 KEYS[2] 访问,而最后的 first 和 second 则是附加参数,可以通过 ARGV[1] 和 ARGV[2] 访问它们。

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

 给出一个RedisTemplate中lua的脚本案例。大致意思是:每次执行这个脚本,让本地变量count去自增1。如果count为1时设置KEYS[1]的过期时间为ARGV[1]。最后返回count的值

final String INCR_ID_LUA = "local count = redis.call('incr',KEYS[1])\n" +
        "if count == 1 then\n" +
        "  redis.call('expire',KEYS[1],ARGV[1])\n" +
        "end\n" +
        "return count";

3、调用

 好了,回归RedisTemplate中,同样的,我们可以用现有的api去调用lua脚本。RedisTemplate中有个execute的方法,事例使用这个重载方法。

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

全实例,解析:脚本中的key[*]对应的execute中的List<K> keys第*个参数,ARGV[*]对应的是Object... args可变参数的第*个参数

    private static final String INCR_ID_LUA = "local count = redis.call('incr',KEYS[1])\n" +
            "if count == 1 then\n" +
            "  redis.call('expire',KEYS[1],ARGV[1])\n" +
            "end\n" +
            "return count";
    public Long test(String key, long time) {
        DefaultRedisScript<Long> defaultRedisScript= new DefaultRedisScript<>();
        defaultRedisScript.setResultType(Long.class);
        defaultRedisScript.setScriptText(INCR_ID_LUA);
        return redisTemplate.execute(defaultRedisScript,Collections.singletonList(key),time);
    }

记录排错

前提:这个方法是我从别的redisUtil里边粘过来的

execute中有个List的参数,原本代码是这样写的

List<String> keys = new ArrayList<>(1);
keys.add(key);

然后就传入到execute中。

在原来redisUtil中是这样的,完全没问题

 可是到我这,就变成了这样,有点迷惑啊???

提示泛型要用Object,然后我就把泛型去掉。

这下好了,也不报错了,然后重启运行代码,nice,不负众望

RedisCommandExecutionException: ERR Error running script (call to f_dd847a1fcf31c329aa47a8e785c63857901f477a): @user_script:3: ERR value is not an integer or out of range

这还是类型问题呗,然后就有了把keys变成Collections.singletonList(key)的操作。重启运行正确一气呵成。但是是哪出问题了呢?

抱着试一试的心态,我终于找向了redistemplate。终于!发现了问题所在。

我复制的redisUtil中

private final RedisTemplate<String, Object> redisTemplate;

而我的是

private final RedisTemplate<Object, Object> redisTemplate;

 源码里:

对应的

 

真相了。。。。。。。

只能说码功尚浅,继续加油吧!!!慢慢积累呗。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐