要实现的功能
job是否挂了?
当前时间减去上次执行时间的耗时,再减去间隔时间,仍然超过阈值(比如3分钟),就告警:某个job可能挂了。
job是否超时?
当前任务耗时超过间隔时间,就告警:某个job超时了。
如果是异步,就设置get的超时时间为间隔时间。如果超时,就告警:某个job超时了。
流程图
实现原理
job是否挂了?
有一个定时任务,扫描所有的job类。
在处理某个job的时候,比较时间:
- 先计算耗时
耗时=当前时间 - 上次执行时间
- 再和阈值比较
耗时 - 间隔时间 > 3m(阈值)
本质,其实是计算上次执行时间距离当前时间是否大于阈值(3m),
- 如果大于
就说明可能挂了
- 如果小于
说明job正常
上次执行时间,何时写入?
在job执行的时候,会写入当前执行时间到redis。
写入的作用?就是用于这个功能:监控job是否挂了。
也就是说,
- 何时写?
业务job执行的时候,写入当前执行时间。
- 何时读?
监控job执行的时候,会获取该业务job的上次执行时间(其实就是第一步的当前执行时间)。然后,比较时间差是否大于阈值(3m)。如果大于,就告警job可能挂了。
job是否超时?
业务job执行的时候,会计算当前任务的耗时,然后拿耗时和阈值(间隔时间)比较。如果大于阈值,就告警:当前业务job超时。
耗时怎么计算?
就是结束时间-开始时间。
如果是异步执行
就基于Future.get(间隔时间),来判断当前任务是否超时。
间隔时间,即超时时间。
本质其实就是,任务执行时间不能超过一个时间间隔周期,否则,就是超时了。
其他
其他的问题,都是细节问题。
监控job多久执行一次?
比如1m。
监控job怎么获取所有的业务job类?
写一个工具类,扫描某个包下面的所有的类。
比如,job类都在xxx.job包目录。
怎么获取间隔时间?
看代码
//获取业务job的间隔时间
CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
cronTriggerImpl.setCronExpression(cron);
List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 3);
Date date1 = dates.get(0);
Date date2 = dates.get(1);
long diffValue = date2.getTime()-date1.getTime();
上次执行时间存储在哪里?
时间的写和读,基于redis实现。
因为是分布式定时任务,是集群,是分片。
分片怎么区分?
key由什么组成?
- 有分片
JobDownException:业务job名字:分片的值
- 无分片
JobDownException:业务job名字
//循环处理每一个业务job类
public void monitorByAnnotation() {
try {
for (Class<?> cls : clazzs) {
ElasticJobConf elasticJobConf = cls.getAnnotation(ElasticJobConf.class);
String key = elasticJobConf.name();
if (elasticJobConf.disabled()) {
continue;
}
String keys[] = new String[elasticJobConf.shardingTotalCount()];
//判断是否有分片
if (elasticJobConf.shardingTotalCount() > 1) { //有分片
String itemParameters = elasticJobConf.shardingItemParameters();
String[] split = itemParameters.split("[,]");
for (int i = 0; i < split.length; i++) {
keys[i] = elasticJobConf.name() + ":" + split[i];
}
} else { //没有分片
keys[0] = elasticJobConf.name();
}
for (String str : keys) {
excuteMethodByAnnotation(elasticJobConf.monitorPort(),elasticJobConf.cron(),str);
}
}
} catch(Exception e){
logger.error("monitorByAnnotation error", e);
}
}