找了很长时间,左试右试的搞了一个redis锁出来,但是被一个更好的方案取代了,有点郁闷,在此记录一下。
开始是因为redis的操作原子性问题,没有用redis自带的key过期,因为setnx 和expire是两个方法,无法保持强一致性,在高并发时候是致命的。而且保留着对redis的自带过期的不信任(这里补充一下,查了资料后发现redis的自带过期还是不错的,两种过期方式,一个是在get的时候判断key是否过期,一个是在空闲时间流出25%cpu时间随机拿带过期的key进行随机淘汰。不过可能在超大的吞吐量的时候可能会因为淘汰不及时,稍微占点内存),就选择了利用value来实现。
@Component
public class NamedLock {
private static final int AUTO_RELEASE_AFTER_MINUTES = 10;
@Autowired
private RedisTemplate redisTemplate;
public boolean tryLock(String name,long useless) {
DateTime now = DateTime.now();
Boolean tryLock = redisTemplate.opsForValue().setIfAbsent(name, now.getMillis());
if (tryLock) {
return true;
} else {
Long lockTime = (Long) redisTemplate.opsForValue().get(name);
if (lockTime == null) {
return redisTemplate.opsForValue().setIfAbsent(name, now.getMillis());
}
if (now.minusMinutes(AUTO_RELEASE_AFTER_MINUTES).isAfter(lockTime)) {
Long originTime = (Long) redisTemplate.opsForValue().getAndSet(name, now.getMillis());
if (originTime == null || originTime.equals(lockTime))
return true;
else
return false;
} else {
return false;
}
}
}
public boolean isLocked(String name) {
return redisTemplate.opsForValue().get(name) == null;
}
public void releaseLock(String name) {
Long lockTime = (Long) redisTemplate.opsForValue().get(name);
if(lockTime != null && DateTime.now().minusMinutes(AUTO_RELEASE_AFTER_MINUTES).isBefore(lockTime)){
redisTemplate.delete(name);
}
return;
}
}
不过后来大神从spring的git里找来一个好方法,就是利用redistemplate的execute,把命令当脚本执行,保证了redis 的setnx和过期的原子性,直接我这个方案就不合适了。还是要多了解api啊。