一: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]則表示客戶端傳遞的第一個和第二個參數