清理郵件的時候發現以前看的一篇關於redis分佈式鎖實現的文章有人回覆- -當時隨意掃了眼文章,爲了防止發生死鎖,思路是使用setnx設置value爲本地時間,而後獲取鎖失敗時讀取value進行時間比對。。而後我回復了下。。多臺應用服務器存在時間不一樣步的問題。。html
其實使用setnx時設置下redis過時時間簡單方便,只是一般在應用程序內經過sdk作這項操做時因爲賦值+設置過時不在同一原子性操做中。。因此不少人以爲不可行了。。那就直接使用lua腳本唄,簡單方便,原子性操做,性能也OK。java
加鎖:aquire_lock_with_timeout.luagit
if redis.call("exists",KEYS[1]) == 0 then local lockSrc = redis.call("setex",KEYS[1],unpack(ARGV)) if lockSrc then return "1" end return "0" end return "-1"
釋放鎖:release_lock.luagithub
if redis.call("get",KEYS[1]) == ARGV[1] then local lockRelease = redis.call("del",KEYS[1]) if lockRelease then return "1" end return "0" end return "-1"
java爲例,加載兩個lua腳本,而後簡單加鎖,釋放鎖redis
@Bean @Qualifier("lockScript") public RedisScript<Integer> acquireLockWithTimeout() { DefaultRedisScript redisScript = new DefaultRedisScript(); redisScript.setLocation(new ClassPathResource("redis/acquire_lock_with_timeout.lua")); redisScript.setResultType(Integer.class); return redisScript; } @Bean @Qualifier("unLockScript") public RedisScript<Integer> releaseLock() { DefaultRedisScript redisScript = new DefaultRedisScript(); redisScript.setLocation(new ClassPathResource("redis/release_lock.lua")); redisScript.setResultType(Integer.class); return redisScript; }
private Integer acquireTimeout;//資源佔有鎖的時間 秒s private Integer acquireInterval;//嘗試獲取鎖的時限 ms private Integer lockTime;//嘗試獲取鎖的時間間隔 ms @Autowired private RedisTemplate redisTemplate; @Autowired @Qualifier("lockScript") private RedisScript<Integer> acquireLockWithTimeout; @Autowired @Qualifier("unLockScript") private RedisScript<Integer> releaseLock; public String tryLock(String lockKey) { String lockValue = UUID.randomUUID().toString(); Long endTime = System.currentTimeMillis() + acquireTimeout; while (System.currentTimeMillis() < endTime) { Integer lockResult = (Integer) redisTemplate.execute(acquireLockWithTimeout, Collections.singletonList(lockKey), lockTime, lockValue); if (lockResult.equals(1)) { return lockValue; } else { try { Thread.sleep(acquireInterval); } catch (InterruptedException ex) { continue; } } } return ""; } public boolean releaseLock(String lockKey, String lockValue) { Integer releaseResult = (Integer) redisTemplate.execute(releaseLock, Collections.singletonList(lockKey), lockValue); if (releaseResult.equals(1)) { return true; } return false; }
固然java也有更復雜更豐富的組件 redisson服務器