最近几个月,现网总是出现定时器不执行的情况,或者定时器卡死的情况,而又不方便排查,只能依靠quartz的debug日志以及错误日志来监控定时器的执行情况,并且随着我们系统中job越来越多,而使得job问题越来越难以跟踪,所以我们才需要一个能过对定时器进行监控的功能,并能实现线程阻塞告警,以及杀死阻塞线程的功能。
监控job有几种方案:
方案一:通过jmx远程或者直接在应用内部定时获取quartz执行信息,可以新增、修改job、job触发器以及执行情况,但是无法对以前执行的job进行跟踪。
方案二:在job的实现类中记录日志,这个方案太麻烦,因为系统目前有很多job实现类,不可能每个都去添加日志。
方案三:代理job执行类,在初始化时使用代理job执行器。
?
最后我选择了方案三。
先让我们来分析下源码,目前只针对quartz1.6.0:
首先查看JobRunShell类,这个是定时器的执行类实现了Runnable接口,它有两个空方法如下:
public class JobRunShell implements Runnable { public void run() { //省略若干代码 try { begin(); } catch (SchedulerException se) { qs.notifySchedulerListenersError("Error executing Job (" + jec.getJobDetail().getFullName() + ": couldn‘t begin execution.", se); break; } //省略若干代码 try { complete(true); } catch (SchedulerException se) { qs.notifySchedulerListenersError("Error executing Job (" + jec.getJobDetail().getFullName() + ": couldn‘t finalize execution.", se); continue; } } protected void begin() throws SchedulerException { } protected void complete(boolean successfulExecution) throws SchedulerException { } }
?很明显,这里预留了两个方法来监控job的执行情况。
所以我们创建了一个其子类来代理它,在开始时记录日志,结束时更新日志,
?
public class MonitorJobRunShell extends JobRunShell { /** * 创建一个新的实例 JobRunShellImpl. * @param jobRunShellFactory * @param scheduler * @param schdCtxt */ public MonitorJobRunShell(JobRunShellFactory jobRunShellFactory, Scheduler scheduler, SchedulingContext schdCtxt) { super(jobRunShellFactory, scheduler, schdCtxt); } @Override protected void begin() throws SchedulerException { super.begin(); try { JobDetail jobDetail = jec.getJobDetail(); quartzLog=getService().insert(jobDetail.getName()); } catch (Exception e) { logger.error("记录job开始时间异常",e); }catch (Throwable e) { logger.error("记录job开始时间出错",e); } } @Override protected void complete(boolean successfulExecution) throws SchedulerException { super.complete(successfulExecution); try { quartzLog.setExeTime(jec.getJobRunTime()); getService().update(quartzLog); } catch (Exception e) { logger.error("记录job结束时间异常",e); }catch (Throwable e) { logger.error("记录job结束时间出错",e); } } }
?创建了该类,必须要让quartz使用我们创建的代理类,这里quartz使用了简单工厂模式,如下
public interface JobRunShellFactory { /** * <p> * Called by the <code>{@link org.quartz.core.QuartzSchedulerThread}</code> * to obtain instances of <code>{@link JobRunShell}</code>. * </p> */ JobRunShell borrowJobRunShell() throws SchedulerException; }
?我们只需要实现该接口,代理原有的std工厂类:
public class StdJobRunShellFactoryProxy implements JobRunShellFactory{ /** * <p> * Called by the <class>{@link org.quartz.core.QuartzSchedulerThread} * </code> to obtain instances of <code> * {@link org.quartz.core.JobRunShell}</code>. * </p> */ public JobRunShell borrowJobRunShell() throws SchedulerException { return new MonitorJobRunShell(this, scheduler, schedCtxt); } /** * <p> * Called by the <class>{@link org.quartz.core.QuartzSchedulerThread} * </code> to return instances of <code> * {@link org.quartz.core.JobRunShell}</code>. * </p> */ public void returnJobRunShell(JobRunShell jobRunShell) { jobRunShell.passivate(); } }
?
进行到这里,需要使用到我们的工厂代理类,这时候则需要代理入口,即StdSchedulerFactory,
public class StdSchedulerFactoryProxy extends StdSchedulerFactory { /** * 初始化Scheduler * 同时,替换JobRunShellFactory,并启动清理job日志线程 * @see org.quartz.impl.StdSchedulerFactory#instantiate(org.quartz.core.QuartzSchedulerResources, org.quartz.core.QuartzScheduler) */ protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) { SchedulingContext schedCtxt = new SchedulingContext(); schedCtxt.setInstanceId(rsrcs.getInstanceId()); Scheduler scheduler = new StdScheduler(qs, schedCtxt); try { JobRunShellFactory jobFactory=new StdJobRunShellFactoryProxy(); jobFactory.initialize(scheduler, schedCtxt); rsrcs.setJobRunShellFactory(jobFactory); } catch (SchedulerConfigException e) { logger.error("初始化MonitorStdJobRunShellFactory出错",e); } return scheduler; } }
?
最后在初始化Scheduler时使用我们代理的Scheduler工厂类就行,实例如下:
StdSchedulerFactory factory = new StdSchedulerFactoryProxy();
这里我们就实现了自己的quartz监控程序,日志记录方式可以自己扩展。这样可以有效方便的监控job的执行情况,日志中可以记录job的执行时长、线程id等,可以配置阈值如果超时可以在界面上kill该线程。
?
?
?
原文:http://lipeng200819861126-126-com.iteye.com/blog/2290769