最近几个月,现网总是出现定时器不执行的情况,或者定时器卡死的情况,而又不方便排查,只能依靠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该线程。

quartz监控日志(一)的更多相关文章

  1. 学习总结 之 WebApi服务监控 log4net记录监控日志

    在请求WebApi 的时候,我们更想知道在请求数据的时候,调用了哪个接口传了什么参数过来,调用这个Action花了多少时间,有没有人恶意请求.我们可以通过记录日志,对Action进行优化,可以通过日志 ...

  2. log4net 记录MVC监控日志

    由于MVC自身的特点,可以让我们记录每一个Controller下Action的执行时间以及View视图渲染完成的时间,本文采用log4net记录MVC每个Action的执行时间和View视图渲染完成时 ...

  3. zabbix监控日志文件

    环境: 操作系统:centos 6.8  ,zabbix软件版本:zabbix 3.0.1 前提条件:zabbix客户端已经配置了主动模式,如何配置主动模式,请参考此文 监控日志keys 首先要了解k ...

  4. linux 判断文件最后更新时间 实现监控日志是否有输出功能

    linux 判断文件最后更新时间 实现监控日志是否有输出功能. 需求:监控log.txt日志文件,超过一分钟没输出内容就认为是停了,则自动启动程序. 用stat 可以看文件的更新时间stat -c % ...

  5. zabbix 3.x 监控日志文件

    1.启用zabbix主动模式 在zabbix agent端,修改/etc/zabbix/zabbix_agentd.conf ServerActive=服务端IP Hostname=tspnginx0 ...

  6. C# 面向切面编程--监控日志记录方案

    背景:现在公司整体在做监控平台,要求把各个部分的细节都记录下来,在前台页面上有所显示,所以现在需要做的就是一个监控日志的记录工作,今天讲的就是渲染监控日志的例子. 现状:当前的渲染程序没有为监控日志记 ...

  7. (72)zabbix监控日志文件 MySQL日志为例

    一般情况下,日志最先反映出应用当前的问题,在海量日志里面找到我们异常记录,然后记录下来,并且根据情况报警,大家可以监控系统日志.nginx.Apache.业务日志. 这边我拿常见的MySQL日志做监控 ...

  8. 1.8-1.10 大数据仓库的数据收集架构及监控日志目录日志数据,实时抽取之hdfs系统上

    一.数据仓库架构 二.flume收集数据存储到hdfs 文档:http://flume.apache.org/releases/content/1.9.0/FlumeUserGuide.html#hd ...

  9. 基于sentry的前端错误监控日志系统(部署sentry服务器/前端项目部署)-让前端最快的定位到生产问题

    背景 在这越来越发达的网络时代,web应用也是越来越复杂,尤其是前端的开发,也是越来越受重视. 所以在我们前端开发完成后,会有一些列的web应用的上线验证,如自测.QA测试.code review 等 ...

  10. 写一个nginx监控日志

    下面的代码是实现一个nginx监控日志功能,是不是很好玩呢.

随机推荐

  1. .Net Framework使用Autofac实现依赖注入

    .Net Framework使用Autofac实现依赖注入 前言 最近也是找了快2周的工作了,收到的面试邀请也就几个,然后有个面试题目是用asp.net mvc + Entityframework 做 ...

  2. adb连接安卓设备失败failed to start daemon

    adb连接安卓设备失败failed to start daemon Reference:https://blog.csdn.net/whshuo2010/article/details/5109449 ...

  3. ZYNQ:Linux添加I2C-RTC驱动

    硬件情况 使用的是DS1338这款RTC时钟芯片,I2C总线对应到PS端的I2C1. 配置 内核 添加有关的驱动: 因为DS1338用的驱动与DS13307相似,一找发现是同一个配置. CONFIG_ ...

  4. Java子类是否能继承父类上的注解

    子类可以继承到父类上的注解吗? 在编写自定义注解时,可以通过指定@Inherited注解,申明自定义注解是否可以被继承:@Inherited只能实现类上的注解继承. 实现情况可细分为以下几种 未申明@ ...

  5. MySql 数据库、数据表操作

    数据库操作 创建数据库 语法 语法一:create database 数据库名 语法二:create database 数据库名 character set 字符集; 查看数据库 语法 查看数据库服务 ...

  6. MongoDB分片+副本集高可用集群的启停步骤

    ■■ 集群启动步骤 1.先启动各节点的 config server mongod -f ${MongoDir}/conf/config.conf 2.再启动各节点的 shard server mong ...

  7. Ubuntu下的LabVIEW开发

    1 虚拟机的安装 我用的是Virtua Box 的虚拟机,当然也有其他的类似软件:下载虚拟机的网址: https://www.virtualbox.org/wiki/Downloads 自行去下载合适 ...

  8. Spring MVC 中的拦截器的使用“拦截器基本配置” 和 “拦截器高级配置”

    1. Spring MVC 中的拦截器的使用"拦截器基本配置" 和 "拦截器高级配置" @ 目录 1. Spring MVC 中的拦截器的使用"拦截器 ...

  9. Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文

    title: Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文 date: 2024/7/21 updated: 2024/7/21 author: cmdragon excerpt: ...

  10. 深入浅出分析最近火热的Mem0个性化AI记忆层

    最近Mem0横空出世,官方称之为PA的记忆层,The memory layer for Personalized AI,有好事者还称这个是RAG的替代者,Mem0究竟为何物,背后的原理是什么,我们今天 ...