一般,我们在处理定时业务逻辑时,经常使用Spring Schedule这个定时任务组件。但是随着用户量的提升,服务集群部署时这些定时任极有可能相互冲突。这种场景就要用到Redis分布式锁,附录两个基于sping-data-redis不同版本的分布式锁,它的本质是redis的setNX(set if not exists )方法,只有不存在的时候才会赋值,设置成功时返回 1 ,设置失败时返回 0 。这种锁的实现是悲观锁。
低版本:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* redis分布式锁
*/
@Component
public class ScheduledLock {
private static final Logger logger = LoggerFactory.getLogger(ScheduledLock.class);
@Autowired
@Qualifier("redisTemplate_string_string")
private RedisTemplate<String, String> redisTemplate;
/**
* 加锁
*
* @param key
* @param expireMillis 超时毫秒
* @return
*/
public boolean lock(String key, Long expireMillis) {
String value = String.valueOf(expireMillis);
//正常加锁
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
//已存在锁
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期
if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
}
return false;
}
/**
* 解锁
*
* @param key
* @return
*/
public void unlock(String key) {
try {
redisTemplate.opsForValue().getOperations().delete(key);
} catch (Exception e) {
logger.error("【redis分布式锁】解锁异常,{}", e);
}
}
/**
* 获取过期时间
*
* @param minutes
* @return
*/
public Long getExpireMillis(int minutes) {
return System.currentTimeMillis() + minutes*60*1000;
}
}
高版本:
@Component
public class ScheduledLock{
@Autowired
@Qualifier("redisTemplate_string_string")
private RedisTemplate<String, String> redisTemplate;
public boolean getLock(String lockId, long millisecond) {
Boolean success = redisTemplate.opsForValue().setIfAbsent(lockId, "lock",
millisecond, TimeUnit.MILLISECONDS);
return success != null && success;
}
public void releaseLock(String lockId) {
redisTemplate.delete(lockId);
}
}