Quartz利用Redis分布式锁实现Master-Slave模式
背景
Spring+Quartz可以很方便地实现任务调度,也支持cluster模式,参见spring整合quartz并持久化。 但需要在数据库创建12张表。杀鸡焉用牛刀?对于简单的分布式场景,有没有更好的方式呢?答案是确定的。
解决方案
利用Redis分布式锁,判断当前任务是否获得锁,如果获得则为master执行任务,否则就是slave候命。
具体步骤
- 1.设置时间戳
//系统时间+超时时间+1
long timestamp = System.currentTimeMillis() + defaulttimeout + 1;
- 2.判断是否获取锁
//1表示获取锁,0表示未获取,多台server仅有一个会获取锁
long lock = jedis.setnx(lockstamp, timestamp + "");
- 3.标记master/slave
//如果自己获取锁,或者原有的master已经超时,则标记自己为master,同时改变锁的超时时间
if (lock == 1 || (System.currentTimeMillis() > Long.parseLong(jedis
.get(lockstamp)) && System.currentTimeMillis() > Long
.parseLong(jedis.getSet(lockstamp, timestamp + "")))) {
logger.info("I'm master, I will do some job.");
break;
} else {//否则,标记自己为slave,并等待
try {
logger.info("I'm slave, I need to stand by.");
Thread.sleep(10);
} catch (InterruptedException e) {
logger.error("Failed to release lock", e);
}
}
- 4.释放锁
//如果未超时,则释放锁
if (System.currentTimeMillis() < Long.parseLong(jedis.get(lockstamp))) {
jedis.del(lockstamp);
}
完整代码
public void execute() {
long lock = 0;
while (lock != 1) {
long timestamp = System.currentTimeMillis() + defaulttimeout + 1;
lock = jedis.setnx(lockstamp, timestamp + "");
if (lock == 1
|| (System.currentTimeMillis() > Long.parseLong(jedis
.get(lockstamp)) && System.currentTimeMillis() > Long
.parseLong(jedis.getSet(lockstamp, timestamp + "")))) {
logger.info("I'm master, I will do some job.");
break;
} else {
try {
logger.info("I'm slave, I need to stand by.");
Thread.sleep(10);
} catch (InterruptedException e) {
logger.error("Failed to release lock", e);
}
}
}
logger.info("=========Job start=========");
//TODO do something
logger.info("=========Job end=========");
if (System.currentTimeMillis() < Long.parseLong(jedis.get(lockstamp))) {
jedis.del(lockstamp);
}
}