zoukankan      html  css  js  c++  java
  • SpringBoot通过RedisTemplate执行Lua脚本的方法步骤

    参考:

    https://www.jb51.net/article/212010.htm

    https://www.jb51.net/article/179946.htm

    https://www.jianshu.com/p/76bc0e963172

    https://www.letianbiji.com/redis/redis-lua.html

    https://www.jb51.net/article/148833.htm

    Redis从2.6版本开始引入对Lua脚本的支持,通过在服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本,直接在服务端原子的执行多个Redis命令。

    其中,使用EVAL命令可以直接对输入的脚本进行求值:

    1
    2
    redis>EVAL "return 'hello world'" 0
    "hello world"

    使用脚本的好处如下:

    1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延。

    2.原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。

    3.复用:客户端发送的脚本会永久存储在Redis中,意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。

    第一步:redis配置

    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;


    @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;
    }

    }

    第二步:写lua脚本

     redis_lock4.lua的内容:

    if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then
    return redis.call('expire',KEYS[1],ARGV[2])
    else
    return -1000
    end

    解释 :setnx命令, 设置 KEYS[1]的值为ARGV[1], 并设置KEYS[1]的过期时间为ARGV[2]。
    如设置prize_stock的值为100,并设置prize_stock的过期时间为200秒。

    第三步:代码
    Long result = null;
    try {
    //调用lua脚本并执行
    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
    redisScript.setResultType(Long.class);//返回类型是Long
    //lua文件存放在resources目录下的redis文件夹内
    redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_lock4.lua")));
    result = redisTemplate.execute(redisScript, Arrays.asList("prize_stock"), 100, 300);
    System.out.println("lock==" + result);
    } catch (Exception e) {
    e.printStackTrace();
    }

     解释: 

    Arrays.asList("prize_stock") 为KEYS, lua脚本对应 KEYS[1]
    100, 300为参数,lua脚本对应ARGV[1] 和 ARGV[2]。


    ---------------------------------------------------------
    仅记录,没用:
    加锁:
    if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then
    return redis.call('expire',KEYS[1],ARGV[2])
    else
    return -1000
    end

    解锁:

    if redis.call("exists",KEYS[1]) == 0 then
    return 100
    end

    if redis.call('get',KEYS[1]) == ARGV[1] then
    return redis.call('del',KEYS[1])
    else
    return 200
    end
    扣库存:
    if (redis.call('exists', KEYS[1]) == 1) then
    local stock = tonumber(redis.call('get', KEYS[1]))
    local num = tonumber(ARGV[1])
    if (stock >= num) then
    return redis.call('incrby', KEYS[1], 0 - num)
    end
    end
    return -100
    
    


  • 相关阅读:
    2020十一届蓝桥杯-七月省赛题解
    ES6 Generator与C#迭代器
    .NET Core微服务部分知识点
    js点击图片传给一个空的div内放大显示
    Jmeter 中 CSV 如何参数化测试数据并实现自动断言
    安装nvidia-docker2报错E: Unable to locate package nvidia-docker2
    微信小程序-点击事件传递参数
    2020牛客暑期多校训练营(第八场)I Interesting Computer Game K Kabaleo Lite
    Count the Colors
    Basic Gcd Problem
  • 原文地址:https://www.cnblogs.com/maohuidong/p/15195152.html
Copyright © 2011-2022 走看看