初始化JedisResourcePool
RoundRobinJedisPool会 轮流连接当前可用的codisProxy
@ConfigurationProperties(prefix = "codis")
public class CodisProperties {
String zookeeper;
String zkProxyDir;
String authpassword;
@Bean
public JedisResourcePool jodisPoolFactory() {
return RoundRobinJedisPool.create().curatorClient(this.zookeeper, 30000).timeoutMs(2000)
.zkProxyDir(this.zkProxyDir).password(this.authpassword).build();
}
public String getZookeeper() {
return zookeeper;
}
public void setZookeeper(String zookeeper) {
this.zookeeper = zookeeper;
}
public String getZkProxyDir() {
return zkProxyDir;
}
public void setZkProxyDir(String zkProxyDir) {
this.zkProxyDir= zkProxyDir;
}
public String getAuthpassword() {
return authpassword;
}
public void setAuthpassword(String authpassword) {
this.authpassword = authpassword;
}
}
使用原子操作来申请与释放锁
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.codis.jodis.JedisResourcePool;
import redis.clients.jedis.Jedis;
public class CodisDistributedLock {
private static Logger LOGGER = LoggerFactory.getLogger(CodisDistributedLock.class);
private final JedisResourcePool codisPool;
private final String redisKey;
private String redisValue;
/** 默认超时时间, 单位秒 */
private long TIMEOUT_SECOND = 30;
/**
* 构造函数
*/
public CodisDistributedLock(JedisResourcePool pool, String lockName) {
this.codisPool = pool;
this.redisKey = lockName;
}
/**
* 获得分布式锁
*
* @param key
* @return true:获得锁成功, false:获得锁失败
*/
public boolean getLock() {
return lock(TIMEOUT_SECOND);
}
/**
* 获得分布式锁
*
* @param key
* @param timeoutSeconds 客户端异常退出后,锁的自动超时时间
* @return true:获得锁成功, false:获得锁失败
*/
public boolean getLock(long timeoutSeconds) {
if (timeoutSeconds <= 0) {
return false;
}
return lock(timeoutSeconds);
}
private boolean lock(long timeoutSeconds) {
boolean flag = false;
try {
this.redisValue = java.util.UUID.randomUUID().toString();
String status = "";
try (Jedis jedis = codisPool.getResource()) {
// 原子操作:如果不存在锁,则设置锁
status = jedis.set(redisKey, this.redisValue, "NX", "EX", timeoutSeconds);
}
if ("OK".equals(status)) {
flag = true;
}
} catch (Exception ex) {
LOGGER.error("get lock failed:" + redisKey, ex);
}
return flag;
}
/**
* 释放锁
*
* @throws DistributedLockException
*/
public void releaseLock() {
// 在releaseLock前,存在由于锁自动过期,而被其他线程获取同一个锁的可能;
// 保证锁的自动过期时间大于任务最大执行时间,来避免此情况
// 最好是用关系数据库行锁或zookeeper的长连接来维持锁(它们都是基于session来维持锁)
try {
Long result = 0L;
// 原子操作:如果锁的值与当前值相等,则删除锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
try (Jedis jedis = codisPool.getResource()) {
// Codis会将这个脚本分配到参数列表中的第一个key所在的redis上执行
result = (Long) jedis.eval(script, Arrays.asList(redisKey), Arrays.asList(redisValue));
}
if (result == null || result != 1L) {
LOGGER.error("release lock failed:" + this.redisKey + " 已自动超时,可能已被其他线程重新获取锁");
}
} catch (Exception ex) {
LOGGER.error("release lock failed:" + this.redisKey, ex);
}
}
}