Spring Schedule与Redis分布式锁构建分布式任务调度
一、项目中使用的Redis分布式锁命令
1.1 setnx
setnx具有原子性,SET if Not eXists,在set的同时就开始判断是否已经存在
1.2 getset
先get后set,获取旧的值,设置新的值
1.3 expire
为 key设置过期时间
1.4 del
删除已创建的key
1.5 流程图
二、实现代码
2.1 RedisShardedPoolUtil添加setnx方法
//setnx方法
public static Long setnx(String key,String value){
ShardedJedis jedis = null;
Long result = null;
try {
jedis = RedisShardedPool.getJedis();
result = jedis.setnx(key,value);
} catch (Exception e) {
log.error("setnx key:{} value:{} error",key,value,e);
RedisShardedPool.returnBrokenResource(jedis);
return result;
}
RedisShardedPool.returnResource(jedis);
return result;
}
2.2 在Const常量类中添加锁名字
public interface REDIS_LOCK{
String CLOSE_ORDER_TASK_LOCK = "CLOSE_ORDER_TASK_LOCK";
}
2.3 propertise添加过期时间
#closeOrderTaskTime start
close.order.task.time.hour=2
#毫秒数
lock.timeout=50000
#closeOrderTaskTime end
2.4 修改CloseOrderTask关闭订单方法
这里需要解决锁过期的问题,当获取锁成功后,进入closeOrder方法,设置锁的有效期,然后去操作订单,如果订单提前操作完成,则利用del删除锁以便快速释放锁
**存在问题:**在Tomcat集群中,如果Tomcat1获取到锁,刚准备调用closeOrder的时候被关闭了,则整个Tomcat集群都进入死锁状态,这时候可以利用kill方法,获取@Destory注解让tomcat关闭前调用此RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK)删除锁
@Scheduled(cron = "0 */1 * * * ?")
public void closeOrderTaskV2(){
log.info("关闭订单定时任务启动");
//分布式锁锁5秒,设置5秒是为了测试用,生产环境不设置那么短时间
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){
//返回值为1,则代表成功获取锁
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else{
log.info("没有获得分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
log.info("关闭订单定时任务结束");
}
//封装锁,设置锁的有效期,否则之后都是获取到setnxResult的0返回值
private void closeOrder(String lockName){
//设置key为50秒,防止死锁,过期后,锁便会释放
RedisShardedPoolUtil.expire(lockName,50