• SpringBoot+Redis实现分布式锁


     一:Lua脚本

    加锁:

    --[[
    思路:
       1.用2个局部变量接受参数
       2.由于redis内置lua解析器,执行加锁命令
       3.如果加锁成功,则设置超时时间
       4.返回加锁命令的执行结果
    ]]
    local key = KEYS[1]
    local value = KEYS[2]
    
    local rs1 = redis.call('SETNX',key,value)
    if rs1 == true
    then
       redis.call('SETEX', key,3600, value)
    end
    
    return rs1

    解锁:

    --[[
    思路:
       1.接受redis传来的参数
       2.判断是否是自己的锁,是则删掉
       3.返回结果值
    ]]
    local key = KEYS[1]
    local value = KEYS[2]
    
    if redis.call('get',key) == value
    then
        return redis.call('del',key)
    else
        return false
    end

    SpringBoot测试类:

    @SpringBootTest
    public class TestApplicationTests {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private RedisTemplate redisTemplate;
        private static String LOCK_PREFIX = "lock_";
        private static String lockPath = "lock.lua";
        private static String unlockPath = "unlock.lua";
    
        public void execJob() throws IOException {
            //1.先去获取锁
            String key = LOCK_PREFIX + "001";
            String value = "job002";
            Boolean rs = lock(key, value,lockPath);
            System.out.println(rs);
            try {
                if (!rs) {
                    String string = redisTemplate.opsForValue().get(key).toString();
                    logger.info(string);
                } else {
                    logger.info("加锁成功,休息5秒");
                    Thread.sleep(5000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //释放锁
                logger.info("释放锁");
                Boolean rs1 = lock(key, value,unlockPath);
                System.out.println(rs1);
            }
    
        }
    
    
        public Boolean lock(String key, String value,String luaPath) throws IOException {
            DefaultRedisScript<Boolean>   lockScript = new DefaultRedisScript<>();
            ClassPathResource resource = new ClassPathResource(luaPath);
            ResourceScriptSource source = new ResourceScriptSource(resource);
            lockScript.setScriptSource(source);
            lockScript.setResultType(Boolean.class);
            Boolean result = (Boolean) redisTemplate.execute(lockScript, Arrays.asList(key, value));
            return result;
        }
    
    
        @Test
       public void contextLoads() throws IOException {
            execJob();
        }
    
    }

    java日志

    true
    2020-04-16 14:54:45.823  INFO 14988 --- [           main] com.example.test.TestApplicationTests    : 加锁成功,休息5秒
    2020-04-16 14:54:50.823  INFO 14988 --- [           main] com.example.test.TestApplicationTests    : 释放锁
    true

    在睡眠的5秒钟,一直查keys,发现先有锁,后面会释放锁

    27.0.0.1:6379> keys *
    1) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00lock_001"
    2) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00lock_001"
    2) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00lock_001"
    2) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00lock_001"
    2) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00lock_001"
    2) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00lock_001"
    2) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00x0212"
    127.0.0.1:6379> keys *
    1) "xacxedx00x05tx00x0212"
    127.0.0.1:6379>

     二 Lua的字符串

    /**
     * @author WGR
     * @create 2020/4/16 -- 16:50
     */
    @SpringBootTest
    public class RedisTest {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private RedisTemplate redisTemplate;
        private static String LOCK_PREFIX = "lock_";
        private static String lockPath = "lock.lua";
        public static final String UNLOCK_LUA;
        static {
            StringBuilder sb = new StringBuilder();
            sb.append("if redis.call("get",KEYS[1]) == KEYS[2] ");
            sb.append("then ");
            sb.append("    return redis.call("del",KEYS[1]) ");
            sb.append("else ");
            sb.append("    return false ");
            sb.append("end ");
            UNLOCK_LUA = sb.toString();
        }
    
    
        public void execJob() throws IOException {
            //1.先去获取锁
            String key = LOCK_PREFIX + "001";
            String value = "job002";
            Boolean rs = lock(key, value,lockPath);
            try {
                if (!rs) {
                    String string = redisTemplate.opsForValue().get(key).toString();
                    logger.info(string);
                } else {
                    logger.info("加锁成功,休息5秒");
                    Thread.sleep(5000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //释放锁
                logger.info("释放锁");
                DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(UNLOCK_LUA,Boolean.class);
                Boolean result = (Boolean)redisTemplate.execute(redisScript, Arrays.asList(key, value));
                System.out.println(result);
            }
    
        }
    
    
        public Boolean lock(String key, String value,String luaPath) throws IOException {
            DefaultRedisScript<Boolean> lockScript = new DefaultRedisScript<>();
            ClassPathResource resource = new ClassPathResource(luaPath);
            ResourceScriptSource source = new ResourceScriptSource(resource);
            lockScript.setScriptSource(source);
            lockScript.setResultType(Boolean.class);
            Boolean result = (Boolean) redisTemplate.execute(lockScript, Arrays.asList(key, value));
            return result;
        }
    
    
        @Test
        public void contextLoads() throws IOException {
            execJob();
        }
    }
    2020-04-16 17:03:51.214  INFO 17376 --- [           main] com.example.test.RedisTest               : 加锁成功,休息5秒
    2020-04-16 17:03:56.214  INFO 17376 --- [           main] com.example.test.RedisTest               : 释放锁
    true

    注:execute可以换成多个参数的 

     

     

            //注意脚本中KYS[l]和KYS[2] 的写法,它们代表客户端传递的第一个键和第二个键,
            //而ARGV[l]和ARGV[2]则表示客户端传递的第一个和第二个参数
  • 相关阅读:
    [BZOJ4480] JSOI2013 快乐的jyy
    [BZOJ4755] JSOI2016 扭动的回文串
    [BZOJ4754] JSOI2016 独特的树叶
    [BZOJ5179] JSOI2011 任务调度
    [BZOJ4320] SHOI2006 Homework
    [AT2300] Snuke Line
    [洛谷P3974] TJOI2015 组合数学
    [CF331E] Biologist
    [洛谷P4249] WC2007 剪刀石头布
    【BZOJ4548】小奇的糖果
  • 原文地址:https://www.cnblogs.com/dalianpai/p/12714132.html
Copyright © 2020-2023  润新知