最近开发项目遇到一个需求,需要对一个sql进行监控,超时10分钟需要预警发送邮件提醒
刚开始考虑是做sql监控,但这个超时了还需要继续执行,不能停下来,而且做sql监控我只能知道这个sql执行了多久,这个是在执行完毕以后才可以知道总时长的,那怎么去知道这个sql执行有没有超过10分钟呢? 这里我提供一种思路是从java层面去监控这个方法执行时间是否超过10分钟,如果大家有更好的方法可以在评论区评论
解决方案一:这里采用FutureTask开启一个线程调用jdbc执行方法,主方法在10分钟后进行查询是否完成,根据isDone的结果去处理,简单介绍下FutureTask几个常用的方法
- cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
- isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
- isDone方法表示任务是否已经完成,若任务完成,则返回true;
- get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
这里使用的是isDone去完成,当然也可以使用et(long timeout, TimeUnit unit)定时去获取返回结果
上代码
public static void readSqlThred(String sqlTest) {
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
dealWithSql(sqlTest);//执行sql
return "执行完成";
}
});
executor.execute(future);
try {
Thread.sleep(1000*6);
if(future.isDone()) {
System.out.println("sql执行完毕");
}else {
System.out.println("警告,sql未执行完毕,开始发送邮件..............");
}
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
这里我是让线程睡了10分钟,10分钟后去取这个方法是否执行完成
解决方案二,我去请教大佬得出的,感觉要靠谱很多,因为前面那个方法如果批量执行,每一个sql执行快,那我都去等是很没有意义的,而且很耗资源,采用下面的方案
方案二:
建立一张日志表,在执行sql前去插入一条记录,记录状态(如刚开始插入是0-执行中,插入完毕是1-执行完成)和插入及更新时间,执行完毕后去更改这个状态和时间,中途异常try catch后也去更改这个表的状态和时间,然后做一个定时任务,每分钟去查询这个表里面状态为执行中并且更新时间和当前时间相差10分钟的即可,找出结果集,剩下的就可以直接判断去预警了
相对来说如果批量执行sql,建议第二种方案,如果已经确定是哪条sql执行很慢,去做单独监控可以考虑第一种方案,大家可以参考下,有问题大家可以评论区讨论,希望对您有帮助
方案三,时隔一段时间后,再次去看着问题,又有新的方法去实现,建议使用
使用springboot+qutz定时任务去实现,qrtz大家可以自行百度,不过多解释
package com.uc.framework.task;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class MyScheduler {
public static void main(String[] args) throws SchedulerException {
SchedulerFactory schdulerFactory = new StdSchedulerFactory();
Scheduler scherdule = schdulerFactory.getScheduler();
//PrintWordsJob为具体要跑任务的类
JobDetail jobDeatail = JobBuilder.newJob(TestJob.class).withIdentity("job1","group1").build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "triggerGroup1")
.startNow()
//设置定时任务间隔,跑一次
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForTotalCount(10,1))
//设置定时任务,一直跑
//.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatHourlyForever(10))
//设置参数
.usingJobData("hour", 4)
.build();
scherdule.scheduleJob(jobDeatail,trigger);
scherdule.start();
}
}
TESTJOB类
package com.ruoyi.framework.task;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class TestJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取参数
System.out.println(context.getMergedJobDataMap().getIntegerFromString("hour"));
//..........自己的业务逻辑
}
}
repeatSecondlyForTotalCount(10,1),跑10次,每间隔一秒执行一次
此外还有repeatHourlyForTotalCount、repeatMinutelyForTotalCount方法都是大同小异,只是时分秒的区别,大家有兴趣可以看下,这个方法比上面的要好,但是有一个问题,就是这个是相当于启动一个子线程去实现的,如果系统重启时正好有任务在等待执行,这个定时任务可能会丢失,但是qutz有表回去存储具体执行信息,这个待我研究下,不过这个问题要解决解决方案也很多,如:存redis,启动的时候去查询,重新执行一下都可以的