产品要求,系统默认设定一个定时任务,同时用户可以在界面上手动修改定时任务执行的周期时间也可以具体某一个时间点执行任务。
一下是代码:
server类中,重要的三个成员变量
//定期执行任务用到的两个类
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
private ScheduledFuture future;
public static ConcurrentHashMap<String, ScheduledFuture> futureMap = new ConcurrentHashMap<String, ScheduledFuture>(); //存放生成的与执行的调度对象
service初始化时,加载定义的定时任务
/**
* 此方法仅在系统启动的时候调用
* 开发测试的先不初始化,正式用再开
* @return
*/
@PostConstruct
public ScheduledFuture<?> initTask(){
List<MonitorCycle> monitorCycleList = findList();
for (MonitorCycle monitorCycle : monitorCycleList) {
//这里应该是有多少条,启动多少个
String name = "task"+ monitorCycle.getIndexId();
ExecuteUtsMointor executeUtsMointor = new ExecuteUtsMointor( name);
executeUtsMointor.indexId = monitorCycle.getIndexId();
if(StringUtils.isEmpty(monitorCycle.getCycleCron() )) {
String cronStr = null; //"0/5 * * * * *";
cronStr = monitorCycle.getCycleCron();
ScheduledFuture future = null;
//固定时间的启动和周期参数的启动方法不同
if (cronStr.contains("?")) {
log.info(name + "启动周期=" + cronStr);
cronStr = cronStr;
future = threadPoolTaskScheduler.schedule(executeUtsMointor, new CronTrigger(cronStr));
} else {
Date exceDate = null;
String[] crons = cronStr.split(" ");
exceDate = newDateChangeToOldDate(Integer.valueOf(crons[5]), Integer.valueOf(crons[4]), Integer.valueOf(crons[3]),
Integer.valueOf(crons[2]), Integer.valueOf(crons[1]), Integer.valueOf(crons[0]));
log.info(name + "启动周期为固定时间=" + cronStr);
future = threadPoolTaskScheduler.schedule(executeUtsMointor, exceDate);
}
futureMap.put(name, future);
}
// String cronStr = StringUtils.isEmpty(monitorCycle.getCycleCron() ) ? "0/3 * * * * ?" : monitorCycle.getCycleCron();
// log.info( name + "周期=" + cronStr );
// ScheduledFuture future =threadPoolTaskScheduler.schedule(executeUtsMointor,new CronTrigger( cronStr ) );
// futureMap.put(name, future);
}
return future;
}
/**
* 查询所有定义的指标模型执行周期数据
*/
public List<MonitorCycle> findList(){
MonitorCycle dto = new MonitorCycle();
QueryWrapper queryWrapper = new QueryWrapper();
List<MonitorCycle> monitorCycleList = baseMapper.selectList( queryWrapper );
return monitorCycleList;
}
/**
* 更新模型的执行周期
* @param monitorCycle
* @return
*/
@Override
public boolean saveOrUpdate(MonitorCycle monitorCycle) {
this.setValIfEmpty(monitorCycle, this.getSaveBeforeMap());
//处理cron的值
if ( null == IStatusType.CycleTypeEnmu.getValue( monitorCycle.getCrcleType().trim())){
throw new IllegalArgumentException("请选择每天,每周,每月,每年,一次性其中的一个合适的类型");
}
//如果 monitorCycle.getCrcleTime 不是时间格式
String cronStr = doWithCron( monitorCycle.getCrcleTime(), monitorCycle.getCrcleType(), monitorCycle.getCrcleDayOf());
monitorCycle.setCycleCron( cronStr );
Boolean isUpdate = false;
if(StringUtil.IsEmpty(monitorCycle.getId())){
//添加
isUpdate = true;
}else{
MonitorCycle monitorCycleOld = baseMapper.selectById( monitorCycle.getId() );
//有改动的情况下才去更新模型周期的执行
if( !monitorCycle.getCycleCron().equals( monitorCycleOld.getCycleCron())){
isUpdate = true;
}
}
Boolean rest = super.saveOrUpdate(monitorCycle);
if( isUpdate ){
//动态修改模型的执行周期
updateTask( monitorCycle.getIndexId(), monitorCycle.getCycleCron() );
}
return rest;
}
//监测cron格式的合法性
//注意:由于这里的 threadPoolTaskScheduler只支持6位,所有的cron格式和标准的有点区别
/**
*
* @param crcleTime 周期的 时分秒 时间部分
* @param crcleType 监测周期的设置
* @param dayOf 月中的第n天, or 一周中的第几周
* 秒 分 时 月中的第几天 月 月中的星期 只有指定执行方式为一次性才有值
* 0 1 2 3 4 5 6
* java的Scheduled 的cron只支持6位, 第7位不支持,那这里的固定时间得改一下
*/
public String doWithCron(String crcleTime,String crcleType,String dayOf){
System.out.println( "执行周期=" + crcleType + dayOf + " " + crcleTime );
int option = IStatusType.CycleTypeEnmu.getValue( crcleType.trim());
String[] cronStr = new String[6];
//时间部分
cronStr[0] = crcleTime.split(":")[2].trim();
cronStr[1] = crcleTime.split(":")[1].trim();
cronStr[2] = crcleTime.split(":")[0].trim();
//是固定日期的时候,直接生成日期
switch (option) {
case 1:
//每天
//0 0 9 * * ? *
cronStr[3] = "*";
cronStr[4] = "*";
cronStr[5] = "?";
// cronStr[6] = "*";
break;
case 2:
//每周
//0 0 9 ? * 2 *
cronStr[3] = "?";
cronStr[4] = "*";
cronStr[5] = String.valueOf(IStatusType.CronWeekEnmu.getValue(dayOf.trim()));
// cronStr[6] = "*";
break;
case 3:
//每月
//0 0 9 30 * ? *
cronStr[3] = dayOf.trim();
cronStr[4] = "*";
cronStr[5] = "?";
// cronStr[6] = "*";
break;
case 4:
//每年
//0 0 9 10 1 ? *
//此时的dayOf 为 n月-n日
cronStr[3] = dayOf.trim().split("-")[1];
cronStr[4] = dayOf.trim().split("-")[0];
cronStr[5] = "?";
// cronStr[6] = "*";
break;
case 5:
//指定一个固定时间
//0 0 9 5 8 ? 2022
cronStr[3] = dayOf.trim().split("-")[2];
cronStr[4] = dayOf.trim().split("-")[1];
// cronStr[5] = "?";
cronStr[5] = dayOf.trim().split("-")[0];
break;
}
log.info("最终处理处理的值=" + StringUtils.join(cronStr, " ") );
if( StringUtils.join(cronStr, " ").contains("?")){
return StringUtils.join(cronStr, " ");
}else{
return StringUtils.join(cronStr, " ");
}
}
/**
* java8的新日期格式转老的日期类型
*/
public Date newDateChangeToOldDate(int year, int month, int dayOfMonth, int hour, int minute, int second){
LocalDateTime ldt = LocalDateTime.of( 2021, 3, 11, 19, 01, 50);
ZonedDateTime zdt = ldt.atZone( ZoneId.systemDefault());
Long instant = zdt.toEpochSecond()*1000;
Date date = new Date(instant);
return date;
}
/**
* 划重点,具体的修改的地方
* 用户在界面上修改定时周期之后的操作
* object 是 Runnable 实现类 需要执行的定时逻辑
*/
public void updateTask(String indexId, String updateCronStr){
String name = "task"+ indexId ;
ExecuteUtsMointor executeUtsMointor = new ExecuteUtsMointor( name );
executeUtsMointor.indexId = indexId;
// 提前测试用来测试线程1进行对比是否关闭。
ScheduledFuture scheduledFuture = futureMap.get( name );
if ( scheduledFuture != null) {
scheduledFuture.cancel(true);
// 查看任务是否在正常执行之前结束,正常true
boolean cancelled = scheduledFuture.isCancelled();
while (!cancelled) {
//这里的停止,它需要一定的时间,把当前的线程要执行完。
scheduledFuture.cancel(true);
log.info("先停止" + name );
}
String cronStr = null; //"0/5 * * * * *";
//固定时间的启动和周期参数的启动方法不同
if( updateCronStr.contains("?")){
log.info( name + "修改周期=" + cronStr );
cronStr = updateCronStr;
threadPoolTaskScheduler.schedule(executeUtsMointor,new CronTrigger( cronStr ) );
}else{
Date exceDate = null;
String[] crons = updateCronStr.split(" ");
exceDate = newDateChangeToOldDate( Integer.valueOf(crons[5]),Integer.valueOf(crons[4]),Integer.valueOf(crons[3]),
Integer.valueOf(crons[2]),Integer.valueOf(crons[1]),Integer.valueOf(crons[0]) );
log.info( name + "修改周期为固定时间=" + cronStr );
threadPoolTaskScheduler.schedule( executeUtsMointor, exceDate );
}
}
}
/**
* 执行模型运算的线程对象
*/
class ExecuteUtsMointor implements Runnable{
public volatile String indexId;
private String name;
ExecuteUtsMointor(String name) {
this.name = name;
}
@Override
public void run(){
ZonedDateTime zdt = ZonedDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
log.info( name + "执行定时任务" + zdt.format(dtf) );
if( StringUtil.IsNotEmpty( indexId )) {
executeUtsMointorOneIndex(indexId);
log.info( name + "index=" + indexId );
}
}
}
/**
* 手动运行一个指标的所有模型
* @param indexId
* @return
*/
public void executeUtsMointorOneIndex( String indexId){
//是否需要更新预警结果值
MonitorCycle monitorCycle = new MonitorCycle();
monitorCycle.setIndexId(indexId);
monitorCycle.setUpdateTime(new Date());
try {
Integer[] indexValeTypes = {1,2,3,4,5,6,6,7,8};
for (Integer indexValeType : indexValeTypes) {
//1.根据指标信息和,指标值类型,找出具体的模型ID
String mxid = indexModelSummaryService.findModelId(indexId, indexValeType);
//2.执行模型的计算,数据直接写到我们的数据库
utsCommonService.instanceRun(mxid);
}
//4.读取预警配置信息
Instance instance = instanceService.findOneByIndexId(indexId);
//5.调用预警计算的方法
if( null != instance ) {
indexWarningResultService.calWarningResultOfOverall(instance.getId());
}
//6.单个指标操作完毕,更新本实体信息。
monitorCycle.setUpdateStatus("更新完成");
updateObjByIndexId( monitorCycle );
}catch (Exception e){
monitorCycle.setUpdateStatus("更新失败");
updateObjByIndexId( monitorCycle );
throw new RuntimeException("更新模型数据失败", e );
}
}
/**
* 调用外部系统执行模型计算
* @param mxid
* @return 创建目录本身的ID
* @throws UtsBaseException
*/
public Boolean instanceRun(String mxid) throws UtsBaseException {
logger.info("运行模型url=" + instanceRun);
logger.info("参数=" + mxid );
//POST请求
HashMap<String,Object> paramMap = new HashMap<>();
paramMap.put("mxid",mxid );
paramMap.put("vc",utsVc );
String result1 = HttpUtil.post(instanceRun, paramMap);
logger.info("运行返回=" + result1 );
JSONObject jsonObject = JSONObject.parseObject(result1);
if( Consts.SUCCESS_CODE_UTS != jsonObject.getInteger("status")){
throw new UtsBaseException("运行模型失败,详情:" + jsonObject.getString("message") );
}
//这里在每次调完模型之后,应调用更新指标数据的方法
return true;
}