使用RedisTemplate實現簡易的分散式鎖(僅供參考)
阿新 • • 發佈:2018-12-10
package com.*.lock;
import lombok.extern.log4j.Log4j2;
import org.nutz.lang.Strings;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.Jedis;
import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
/**
* @author yangxing
* @since 2018-09-14 10:19
**/
@Log4j2
public class RedisLock {
// 鎖標誌
public static final String PREFIX = "lock_";
// SET_IF_NOT_EXIST 標誌,表示如果當key不存在時進行set操作,否則不處理
private static final String NX = "NX";
// 過期時間標誌, EX 表示以秒作為單位
private static final String EX = "EX";
// 鎖成功標誌
private static final String LOCK_SUCCESS = "OK";
// 解鎖成功標誌
private static final long DEL_SUCCESS = 1L;
// 預設過期時間
private static final int DEFAULT_EXPIRE_TIME = 60;
// 預設重試次數
private static final int DEFAULT_RETRY_TIME = 3;
// 預設重試暫停時間
private static final int DEFAULT_INTERNAL_TIME = 100 ;
// 解鎖時的 lua語言Script
private static final String DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
private RedisTemplate<String, Object> redisTemplate;
// 過期時間
private int expireSeconds;
// 鎖重試次數
private int retryTimes;
// 鎖重試間隔
private int internalTime;
// 加鎖者標誌, 解鎖時只有與加鎖時的值一致才允許解鎖
private final String lockId;
private RedisLock(RedisTemplate<String, Object> redisTemplate) {
this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
}
private RedisLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
this.redisTemplate = redisTemplate;
this.expireSeconds = expireSeconds;
this.retryTimes = retryTimes;
this.internalTime = internalTime;
this.lockId = UUID.randomUUID().toString();
}
public boolean lock(String key) {
if (Strings.isBlank(key)) {
return false;
}
final String fullKey = PREFIX + key;
log.debug("嘗試獲取鎖, 鎖Key:{}, 鎖標識:{}", fullKey, lockId);
for (int i = 0; i < retryTimes; i++) {
log.debug("第 {} 次嘗試獲取鎖", i + 1);
String status = redisTemplate.execute((RedisCallback<String>) connection -> {
String result;
try (Jedis jedis = (Jedis) connection.getNativeConnection()) {
result = jedis.set(fullKey, lockId, NX, EX, expireSeconds);
}
return result;
});
if (LOCK_SUCCESS.equalsIgnoreCase(status)) {
log.debug("獲取鎖成功, 鎖Key:{}, 鎖標識:{}", fullKey, lockId);
return true;
}
try {
Thread.sleep(internalTime);
} catch (InterruptedException e) {
break;
}
}
log.debug("嘗試獲取鎖 {} 次後失敗, 鎖Key:{}", retryTimes, fullKey);
return false;
}
public boolean unlock(String key) {
if (Strings.isBlank(key)) {
return false;
}
final String fullKey = PREFIX + key;
log.debug("嘗試解鎖, 鎖Key:{}, 鎖標識:{}", fullKey, lockId);
Object value = redisTemplate.execute((RedisCallback<Object>) connection -> {
Object result;
try (Jedis jedis = (Jedis) connection.getNativeConnection()) {
result = jedis.eval(DEL_SCRIPT,
Collections.singletonList(fullKey), Collections.singletonList(lockId));
}
return result;
});
boolean isUnlock =
Objects.nonNull(value) &&
value instanceof Long &&
DEL_SUCCESS == (Long) value;
if (isUnlock) {
log.debug("解鎖成功, 鎖Key:{}", fullKey);
}
return isUnlock;
}
public String getLockId() {
return lockId;
}
public static RedisLock getLock(RedisTemplate<String, Object> redisTemplate) {
return new RedisLock(redisTemplate);
}
public static RedisLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds) {
return new RedisLock(redisTemplate, expireSeconds, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
}
}