前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >quartz监控日志(一)

quartz监控日志(一)

作者头像
一笠风雨任生平
发布2019-08-02 11:10:39
2K0
发布2019-08-02 11:10:39
举报
文章被收录于专栏:服务化进程

最近几个月,现网总是出现定时器不执行的情况,或者定时器卡死的情况,而又不方便排查,只能依靠quartz的debug日志以及错误日志来监控定时器的执行情况,并且随着我们系统中job越来越多,而使得job问题越来越难以跟踪,所以我们才需要一个能过对定时器进行监控的功能,并能实现线程阻塞告警,以及杀死阻塞线程的功能。

监控job有几种方案:

方案一:通过jmx远程或者直接在应用内部定时获取quartz执行信息,可以新增、修改job、job触发器以及执行情况,但是无法对以前执行的job进行跟踪。

方案二:在job的实现类中记录日志,这个方案太麻烦,因为系统目前有很多job实现类,不可能每个都去添加日志。

方案三:代理job执行类,在初始化时使用代理job执行器。

最后我选择了方案三。

先让我们来分析下源码,目前只针对quartz1.6.0:

首先查看JobRunShell类,这个是定时器的执行类实现了Runnable接口,它有两个空方法如下:

代码语言:javascript
复制
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的执行情况。

所以我们创建了一个其子类来代理它,在开始时记录日志,结束时更新日志,

代码语言:javascript
复制
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使用了简单工厂模式,如下

代码语言:javascript
复制
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工厂类:

代码语言:javascript
复制
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,

代码语言:javascript
复制
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该线程。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档