在Spring Schedule进行定时关单任务时候呢,由于项目涉及到分布式,导致遇到多线程上的问题,
最后在Spring Schedule定时关单快速入门(三)的最后也得到了解决。但是解决的方法相对原生。
至此,我们引入Redisson框架进行优化出v4版本的定时器。
1.首先我们要在项目中的pom.xml文件中引入Redisson框架。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.9.0</version>
</dependency>
2.在项目中的pom.xml文件中引入Redisson框架的依赖文件。
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.9.0</version>
</dependency>
3.将Redisson进行初始化
A:首先创建Redisson类,在类上添加上Spring的注解(@Component)使该类被Spring管理。
B:创建两个主要类成员(config)(redisson)和对应Redis的类成员(RedisIP)(RedisPort)
config为配置文件,负责加载上Redis的类成员(RedisIP)(RedisPort)
redisson为主要成员,负责加载上配置文件(config)
C:添加初始化方法,为方法添加上@PostConstruct注解使该方法在spring加载此类组件(Component)的时候调用此方法。
D:为主要成员(redisson)提供外界访问方式(getter方法)
Redisson初始化代码如下:
package com.mmall.common;
import com.mmall.util.PropertiesUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@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");//RedisIP
private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));//RedisPort
private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));
@PostConstruct //在spring加载此类组件(Component)的时候调用此方法。
private void init(){//初始化方法
//setAddress 需要跟进源码,源码里头有该方法的使用介绍,里头详细的介绍了参数该如何传递
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);
e.printStackTrace();
}
}
//如果不添加下面这个方法,那项目在关闭Tomcat的时候会有线程未关闭。导致Tomcat无法顺利关闭。(重点)
@PreDestroy //被@PreConstruct修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreConstruct修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。
private void close(){
redisson.shutdown();
log.info("Redisson关闭");
}
}
定时关单器V4版本(代码)
@Scheduled(cron = "0 */1 * * * ?")//每1分钟(每个1分钟的整数倍)调用一次
public void closeOrderTaskV4(){
RLock lock = redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);//获取到锁 参数传递锁的名字
boolean getLock = false;
try { //在参数1中的等待时间中,我们应该预估定时器代码的执行而定,
//如果项目中定的等待时间是2秒,但是定时器的代码在1秒的时候执行完后释放了锁
//此时另一个Tomcat的定时器发现在等待2秒的期间发现锁释放了,随即进入了锁定时器。
//所以在我们不知道定时器代码执行的大概时间时,我们尽可能将等待时间设置为0
if(getLock = lock.tryLock(0,5, TimeUnit.SECONDS)){//参数1:等待时间2 参数2:工作时间5 参数3:单位秒
log.info("Redisson获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
//iOrderService.closeOrder(hour);
}else{
log.info("Redisson没有获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
}
} catch (InterruptedException e) {
log.error("Redisson分布式锁获取异常",e);
}finally {
if(!getLock){
return;
}
lock.unlock();//释放锁
log.info("Redisson分布式锁释放锁");
}
}