定時器+redis分散式鎖、定時器+redisson框架分散式鎖
阿新 • • 發佈:2018-12-17
定時器+redis分散式鎖
在xml中新增:
<task:annotation-driven/>
1. 不帶鎖的定時器:
2. 帶redis分散式鎖的定時器:
原理:設定鎖的lokkey,值為當前毫秒值+超時毫秒值,redis的setnx方法為,如果不存在lokkey,返回1並設定lokkey,返回0,代表已存在鎖
程式碼
3.上面的方法雖然好,但是如果在設定鎖的有效期之前,tomcat異常關閉,比如:殺死tomcat程序,在redis中已經存在鎖,這種情況會出現死鎖,所以需要進行改進
優化版:設定鎖的lokkey,值為當前毫秒值+超時毫秒值,redis的setnx方法為,如果不存在lokkey,返回1並設定lokkey,返回0,代表已存在鎖
返回0時,判斷當前時間是否為空&&是否 > redis的value值 lockvalueA
{
不為空且大於:使用getset方法,設定值為當前毫秒+超時毫秒,返回舊值 lockvalueB,判斷lockvalueB是否為空 || (lockvalueB不為空且lockvalueA==lockvalueB)
{
lockvalueB為空:代表走到這一步,別的tomcat已經執行了一遍程式碼並且釋放了鎖
lockvalueB不為空且lockvalueA==lockvalueB:代表開啟定時後第一次執行此程式碼
lockvalueA != lockvalueB:代表別的tomcat已經在執行此程式碼,並且鎖還未被釋放
}
不為空且小於:結束
為空:結束
}
程式碼
@Scheduled(cron="0 */1 * * * ?") public void closeOrderTaskV3(){ log.info("關閉訂單定時任務啟動"); long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000")); Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout)); if(setnxResult != null && setnxResult.intValue() == 1){ closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK); }else{ //未獲取到鎖,繼續判斷,判斷時間戳,看是否可以重置並獲取到鎖 String lockValueStr = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK); if(lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)){ String getSetResult = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout)); //再次用當前時間戳getset。 //返回給定的key的舊值,->舊值判斷,是否可以獲取鎖 //當key沒有舊值時,即key不存在時,返回nil ->獲取鎖 //這裡我們set了一個新的value值,獲取舊的值。 if(getSetResult == null || (getSetResult != null && StringUtils.equals(lockValueStr,getSetResult))){ //真正獲取到鎖 closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK); }else{ log.info("沒有獲取到分散式鎖:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK); } }else{ log.info("沒有獲取到分散式鎖:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK); } } log.info("關閉訂單定時任務結束"); }
定時器+redisson框架分散式鎖
使用這個方法程式碼會簡單很多
在pom.xml中新增:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.9.0</version>
</dependency>
RedissonManager類
@Component
@Slf4j
public class RedissonManager {
private Config config = new Config();
private Redisson redisson = null;
public Redisson getRedisson() {
return redisson;
}
private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));
private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));
@PostConstruct
private void init(){
try {
config.useSingleServer().setAddress(new StringBuilder().append(redis1Ip).append(":").append(redis1Port).toString());
redisson = (Redisson) Redisson.create(config);
log.info("初始化Redisson結束");
} catch (Exception e) {
log.error("redisson init error",e);
}
}
}