• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    基于Redis实现分布式锁的方法(lua脚本版)

    1、前言

    在Java中,我们通过锁来避免由于竞争而造成的数据不一致问题。通常我们使用synchronized 、Lock来实现。但是Java中的锁只能保证在同一个JVM进程内中可用,在跨JVM进程,例如分布式系统上则不可靠了。

    2、分布式锁

    分布式锁,是一种思想,它的实现方式有很多,如基于数据库实现、基于缓存(Redis等)实现、基于Zookeeper实现等等。为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件

     3、基于Redis实现分布式锁

    以下代码实现了基于redis中间件的分布式锁。加锁的过程中为了保障setnx(设置KEY)和expire(设置超时时间)尽可能在一个事务中,使用到了lua脚本的方式,将需要完成的指令一并提交到redis中;

    3.1、RedisConfig.java

    package com.demo.configuration;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    @Configuration
    public class RedisConfig {
    
        @Bean
        public RedisTemplateString, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplateString, Object> template = new RedisTemplate>();
            template.setConnectionFactory(factory);
            // key采用String的序列化方式
            template.setKeySerializer(new StringRedisSerializer());
            // value序列化方式采用jackson
            template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            template.afterPropertiesSet();
            return template;
        }
    
    }

    3.2、RedisLockController.java

    package com.demo.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.script.DefaultRedisScript;
    import org.springframework.scripting.support.ResourceScriptSource;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Arrays;
    
    @RestController
    @RequestMapping("/redis")
    public class RedisLockController {
    
        @Autowired
        private RedisTemplateString, Object> redisTemplate;
    
        @RequestMapping(value = "/lock/{key}/{uid}/{expire}")
        public Long lock(@PathVariable("key") String key, @PathVariable("uid") String uid, @PathVariable("expire") Integer expire) {
            Long result = null;
            try {
                //调用lua脚本并执行
                DefaultRedisScriptLong> redisScript = new DefaultRedisScript>();
                redisScript.setResultType(Long.class);//返回类型是Long
                //lua文件存放在resources目录下的redis文件夹内
                redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_lock.lua")));
                result = redisTemplate.execute(redisScript, Arrays.asList(key), uid, expire);
                System.out.println("lock==" + result);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
        @RequestMapping(value = "/unlock/{key}/{uid}")
        public Long unlock(@PathVariable("key") String key, @PathVariable("uid") String uid) {
            Long result = null;
            try {
                //调用lua脚本并执行
                DefaultRedisScriptLong> redisScript = new DefaultRedisScript>();
                redisScript.setResultType(Long.class);//返回类型是Long
                //lua文件存放在resources目录下的redis文件夹内
                redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_unlock.lua")));
                result = redisTemplate.execute(redisScript, Arrays.asList(key), uid);
                System.out.println("unlock==" + result);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
    }

    3.3、redis_lock.lua

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

    3.4、redis_unlock.lua

    if redis.call("exists",KEYS[1]) == 0 then
        return 1
    end
    
    if redis.call('get',KEYS[1]) == ARGV[1] then
        return redis.call('del',KEYS[1])
    else
        return 0
    end

    4、测试效果

    key123为key,thread12345为value标识锁的主人,300为该锁的超时时间

    加锁:锁主人为thread12345
    http://127.0.0.1:8080/redis/lock/key123/thread12345/300

    解锁:解锁人为thread123456
    http://127.0.0.1:8080/redis/unlock/key123/thread123456

    解锁:解锁人为thread12345
    http://127.0.0.1:8080/redis/unlock/key123/thread12345

    4.1、加锁,其他人解锁


    thread12345加的锁,thread123456是解不了的,只有等thread12345自己解锁或者锁的超时时间过期

    4.2、加锁,自己解锁


    thread12345加的锁,thread12345自己随时可以解锁,也可以等锁的超时时间过期

    5、总结

    到此这篇关于基于Redis实现分布式锁的方法(lua脚本版)的文章就介绍到这了,更多相关Redis实现分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • 详解redis分布式锁的这些坑
    • SpringBoot之使用Redis实现分布式锁(秒杀系统)
    • 详解Redis 分布式锁遇到的序列化问题
    • 详解RedisTemplate下Redis分布式锁引发的系列问题
    • redisson分布式锁的用法大全
    • php基于redis的分布式锁实例详解
    • Redis分布式锁升级版RedLock及SpringBoot实现方法
    • 利用redis实现分布式锁,快速解决高并发时的线程安全问题
    • 详解基于redis实现分布式锁
    上一篇:redis三种高可用方式部署的实现
    下一篇:基于Redis延迟队列的实现代码
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    基于Redis实现分布式锁的方法(lua脚本版) 基于,Redis,实现,分布式,