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) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\block_001"
2) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\block_001"
2) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\block_001"
2) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\block_001"
2) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\block_001"
2) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\block_001"
2) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x0212"
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x0212"
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]則表示客戶端傳遞的第一個和第二個參數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM