用这种方案仍然有问题
1.锁的互斥性失效
客户端一拿到锁 执行很长时间 超过了过期时间
客户端二拿到了这把锁 这样互斥性失效 (#加长过期时间)
2.客户端与redis的系统时间的不一致问题
等等使用下面方案还是有这些缺点 ,暂时还不知道如何解决
科普知识
setnx(key,value);
getset(key,vlaue); 查看
分布式锁的调用客户端模板代码
public void startMessageAskedChecker() {
boolean acquired = false;
String lock = "redis:lock:message:ask:checker";
try {
acquired = distributeLock.tryLock(lock, 3000);//3000为requestTimeout
if (acquired) {
logger.info("start ask checker ");
messageAskedChecker.execute();
} else {
logger.info("未获取到执行权限...");
}
} catch (Exception e) {
logger.error("ask checker error ", e);
} finally {
if (acquired) {
distributeLock.unLock(lock);
}
}
}
---------------------------------------
GETSET lock.foo <current Unix time + lock timeout + 1>
通过GETSET,C3拿到的时间戳如果仍然是超时的,那就说明,C3如愿以偿拿到锁了。
如果在C3之前,有个叫C4的客户端比C3快一步执行了上面的操作,那么C3拿到的时间戳是个未超时的值,这时,C3没有如期获得锁,需要再次等待或重试。留意一下,尽管C3没拿到锁,但它改写了C4设置的锁的超时值,不过这一点非常微小的误差带来的影响可以忽略不计。
接口实现类
package com.liangbo.xing.flexibletranscation.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import java.util.concurrent.TimeUnit;
@Component
public class DistributeLockImpl implements DistributeLock {
private static final Logger LOG = LoggerFactory.getLogger(DistributeLockImpl.class);
private static final long DEFAULT_LOCK_EXPIRE_TIME = 10000;
private static final long DEFAULT_SLEEP_TIME = 100;
// @Autowired
// private JedisCluster jedisCluster;
@Autowired
private Jedis jedis;
@Autowired
public DistributeLockImpl() {
}
@Override
public boolean tryLock(String lock, long requestTimeout) throws Exception {
return tryLock(lock, DEFAULT_LOCK_EXPIRE_TIME, requestTimeout);
}
@Override
public boolean tryLock(String lock, long lockExpireTime, long requestTimeout) throws Exception {
if (lockExpireTime <= 0 || requestTimeout <= 0) {
return false;
}
while (requestTimeout > 0) {
String expire = String.valueOf(System.currentTimeMillis() + lockExpireTime + 1);
// Long result = jedisCluster.setnx(lock, expire);
Long result = jedis.setnx(lock, expire);
if (result == 1L) {
return true;
}
// String currentValue = jedisCluster.get(lock);
String currentValue = jedis.get(lock);
if (currentValue == null) {
continue;
} else if (Long.parseLong(currentValue) < System.currentTimeMillis()) {
//当前时间 大于 缓存设置的时间 锁已经失效可以被使用
// String oldValue = jedisCluster.getSet(lock, expire);
String oldValue = jedis.getSet(lock, expire);
if (oldValue == null || oldValue.equals(currentValue)) {
return true;
}
}
long sleepTime;
if (requestTimeout > DEFAULT_SLEEP_TIME) {
sleepTime = DEFAULT_SLEEP_TIME;
requestTimeout -= DEFAULT_SLEEP_TIME;
} else {
sleepTime = requestTimeout;
requestTimeout = 0;
}
TimeUnit.MILLISECONDS.sleep(sleepTime);
}
return Boolean.FALSE;
}
@Override
public void unLock(String lock) {
// String value = jedisCluster.get(lock);
String value = jedis.get(lock);
if (value != null && Long.parseLong(value) > System.currentTimeMillis()) {
// jedisCluster.del(lock);
jedis.del(lock);
}
}
}
接口类
package com.liangbo.xing.flexibletranscation.service;
public interface DistributeLock {
boolean tryLock(String lock, long requestTimeout) throws Exception;
boolean tryLock(String lock, long lockExpireTime, long requestTimeout) throws Exception;
void unLock(String lock);
}