标签:quartz schedulerfactorybean 可视化
本文介绍的是使用spring(spring继承并简化的quartz)的作业框架时,作业的可视化(管理,本文的可视化管理做的不完善)
解决办法就是使用监听器,实现job历史记录、job统计、异常记录、手动触发job
先来看一下 实现的流程
这里只需要自己实现几个监听器并注册即可
- StatisticSchedulerListener 监听触发规则triggers,一般是在spring启动和刷新时触发
- StatisticJobListener JOB监听(监听JOB执行前和执行后)
- StatisticTriggerListener
这里主要使用 StatisticJobListener
public class StatisticJobListener implements JobListener { private static Logger log = Logger.getLogger(StatisticJobListener.class); private static ThreadLocal<JobStatisticBean> threadLocal = new ThreadLocal<JobStatisticBean>(); private String name; public void setName(String name) { this.name = name; } @Override public String getName() { return this.name; } //beginging @Override public void jobToBeExecuted(JobExecutionContext context) { log.info("JOB将要执行... "+context.getJobDetail().getName()); JobStatisticBean jobStatisticBean = new JobStatisticBean(); jobStatisticBean.setJobName(context.getJobDetail().getName()); jobStatisticBean.setJobDetail(context.getJobDetail()); jobStatisticBean.setTrigger(context.getTrigger()); jobStatisticBean.setStartTime(new Timestamp(System.currentTimeMillis())); jobStatisticBean.setJobExecutionContext(context); jobStatisticBean.setJobIntance(context.getJobInstance()); threadLocal.set(jobStatisticBean); } @Override public void jobExecutionVetoed(JobExecutionContext context) { log.info("jobExecutionVetoed "+context.getJobDetail().getName()); } @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { log.info("JOB执行完毕 "+context.getJobDetail().getName()); JobStatisticBean jobStatisticBean = threadLocal.get(); if(jobStatisticBean!=null){ jobStatisticBean.setEndTime(new Timestamp(System.currentTimeMillis())); jobStatisticBean.setTakesTime(jobStatisticBean.getEndTime().getTime()-jobStatisticBean.getStartTime().getTime()); StatisticProcessor.add(jobStatisticBean); } } }
public class StatisticProcessor extends Thread implements ApplicationContextAware, InitializingBean, DisposableBean { private static Logger logJob = Logger.getLogger(LoggerCategory.JOB_STATISTIC); private static Logger logJobHistory = Logger.getLogger(LoggerCategory.JOB_STATISTIC_HISTORY); private static Logger logJobException = Logger.getLogger(LoggerCategory.JOB_STATISTIC_EXCEPTION); private static volatile Queue<JobStatisticBean> jobQueue = new ConcurrentLinkedQueue<JobStatisticBean>(); private static volatile Queue<SchedulerExceptionBean> exceptionQueue = new ConcurrentLinkedQueue<SchedulerExceptionBean>(); private static volatile Map<String, JobStatisticBean> jobMap = new ConcurrentHashMap<String, JobStatisticBean>(); /** * 手动触发JOB * @param jobStatisticBean * @return * @throws Exception */ public static boolean runByHand(JobStatisticBean jobStatisticBean) throws Exception{ if(jobStatisticBean==null) return false; JobExecutionContext context = jobStatisticBean.getJobExecutionContext(); Job job = jobStatisticBean.getJobIntance(); job.execute(context); return true; } /** * 添加一个JOB的实例(使用map防止重复),并记录JOB执行记录(Queue) * @param jobStatisticBean * @return */ public static boolean add(JobStatisticBean jobStatisticBean){ if(!jobMap.containsKey(jobStatisticBean.getJobName())) { logJob.info("add job "+ JacksonUtil.toJsonString(jobStatisticBean)); } jobMap.put(jobStatisticBean.getJobName(), jobStatisticBean); logJobHistory.info(JacksonUtil.toJsonString(jobStatisticBean)); return jobQueue.add(jobStatisticBean); } /** * 添加执行异常LOG * @param schedulerExceptionBean * @return */ public static boolean add(SchedulerExceptionBean schedulerExceptionBean){ logJobException.info(JacksonUtil.toJsonString(schedulerExceptionBean)); return exceptionQueue.add(schedulerExceptionBean); } public static List<JobStatisticBean> getALlJobs() { int size = jobMap.size(); size = size>100? 100:size; return Arrays.asList(jobMap.values().toArray(new JobStatisticBean[size])); } public static List<JobStatisticBean> getAllJobHistory() { int size = jobQueue.size(); size = size>100? 100:size; return Arrays.asList(jobQueue.toArray(new JobStatisticBean[size])); } public static List<SchedulerExceptionBean> getAllExceptions(){ int size = exceptionQueue.size(); size = size>100? 100:size; return Arrays.asList(exceptionQueue.toArray(new SchedulerExceptionBean[size])); } private volatile boolean exit = false; private ApplicationContext applicationContext = null; private static volatile StatisticProcessor processorIntance = null; public synchronized void init(){ if(processorIntance==null) { logJob.info("--------------------- "+new Date().toLocaleString()+" --------------------------"); logJobHistory.info("--------------------- "+new Date().toLocaleString()+" --------------------------"); logJobException.info("--------------------- "+new Date().toLocaleString()+" --------------------------"); processorIntance = new StatisticProcessor(); processorIntance.setName(GConstants.THREA_HEAD + "JobStatisticProcessor"); processorIntance.setDaemon(true); processorIntance.start(); } } @Override public void run() { do{ try { Thread.sleep(1000*30); synchronized (jobQueue) { if (!jobQueue.isEmpty()) { long outSize = jobQueue.size() - 100; while (outSize-- > 0) { jobQueue.poll(); } } } synchronized (exceptionQueue) { if (!exceptionQueue.isEmpty()) { long outSize = exceptionQueue.size() - 100; while (outSize-- > 0) { exceptionQueue.poll(); } } } }catch (Exception e){ e.printStackTrace(); } }while( !this.exit); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() throws Exception { this.init(); } @Override public void destroy(){ this.exit = true; } }
public class JobStatisticBean implements Serializable { private static long serialVersionUID = -1L; private String jobName; private String className; private Timestamp startTime; private Timestamp endTime; private Long takesTime; private JobDetail jobDetail; private Trigger trigger; private JobExecutionContext jobExecutionContext; private Job jobIntance; //.. getter & setter }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false"> <property name="schedulerName" value="EZhe-taks" /> <property name="triggers"> <list> <ref bean="task1" /> <ref bean="task2" /> </list> </property> <property name="globalJobListeners" > <list> <bean class="com.gozap.ezhe.task.listener.StatisticJobListener" > <property name="name" value="JOB统计" /> </bean> </list> </property> <property name="schedulerListeners"> <list> <bean class="com.gozap.ezhe.task.listener.StatisticSchedulerListener"> </bean> </list> </property> <property name="autoStartup" value="true"/> <property name="configLocation" value="classpath:quartz.properties" /> </bean> <!--自动更新汇率任务--> <bean id="task1" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" > <bean id="jobUpdateCurrencyTask" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="name" value="自动更新汇率任务" /> <property name="jobClass" value="com.gozap.ezhe.task.CurrencyUpdateTask"></property> </bean> </property> <property name="cronExpression" value=" 0 0/30 * * * ?"/> </bean> <!--自动xx的任务--> <bean id="task2" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" > <bean id="logisticsScheduleTask" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="name" value="自动xx的任务" /> <property name="jobClass" value="com.gozap.ezhe.task.LogisticsScheduleTask"></property> </bean> </property> <property name="cronExpression" value="0 0 0/2 * * ?"/> </bean> </beans>
JobStatisticAction.java
public class JobStatisticAction extends BaseAction { private List<JobStatisticBean> jobList; private List<JobStatisticBean> jobHistoryList; private List<SchedulerExceptionBean> jobExceptionList; public String jobStatistic() throws Exception { jobList = StatisticProcessor.getALlJobs(); jobHistoryList = StatisticProcessor.getAllJobHistory(); Collections.reverse(jobHistoryList); jobExceptionList = StatisticProcessor.getAllExceptions(); return Action.SUCCESS; } //============================ public List<JobStatisticBean> getJobList() { return jobList; } public List<JobStatisticBean> getJobHistoryList() { return jobHistoryList; } public List<SchedulerExceptionBean> getJobExceptionList() { return jobExceptionList; } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="/struts-tags" prefix="s" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="java.util.Date" %> <% SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String nowTime = sdf.format(new Date()); %> <html> <head> <title>Job Statistic</title> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <!-- 新 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css"> <!-- 可选的Bootstrap主题文件(一般不用引入) --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap-theme.min.css"> <!-- jQuery文件。务必在bootstrap.min.js 之前引入 --> <script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script> <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> <style> table{ font-size: smaller; } </style> </head> <body> <div class="container "> <div class="panel panel-success"> <div class="panel-heading"> <h2 class="text-center">Job List</h2> </div> <div class="panel-body"> <table class="table-hover table"> <tr> <th>序号</th> <th style="width: 200px;">任务名称</th> <th style="min-width: 180px;">最近执行时间</th> <th style="width: 100px;">耗时(ms)</th> <th style="width: 100px;">More [系统时间: <font color="red" font-size="120%"> <%=nowTime%> </font> ]</th> </tr> <s:iterator value="jobList" id="jobList" status="step"> <tr> <td>${step.index+1}</td> <td>${jobName}</td> <td>${startTime} - ${endTime}</td> <td>${takesTime}</td> <td> <table style="font-size: 100%;"> <tr> <td align="right">Class :</td> <td> ${jobDetail.jobClass} </td> </tr> <tr> <td align="right">CronEx :</td> <td> ${trigger.cronExpression} </td> </tr> <tr> <td align="right">NextFireTime :</td> <td> <s:date name="trigger.nextFireTime" format="yyyy-MM-dd HH:mm:ss.SSS" /></td> </tr> <tr> <td align="right">PreFireTime :</td> <td> <s:date name="trigger.previousFireTime" format="yyyy-MM-dd HH:mm:ss.SSS" /> </td> </tr> <tr> <td align="right">StartTime :</td> <td> <s:date name="trigger.startTime" format="yyyy-MM-dd HH:mm:ss.SSS" /> </td> </tr> </table> </td> </tr> </s:iterator> </table> </div> </div> <div class="panel panel-danger"> <div class="panel-heading"> <h2 class="text-center">Job Error</h2> </div> <div class="panel-body"> <table class="table-hover table"> <tr> <th>序号</th> <th style="width: 200px;">错误内容</th> <th style="min-width: 180px;">堆栈</th> </tr> <s:iterator value="jobExceptionList" id="jobList" status="step"> <tr> <td>${step.index+1}</td> <td>${msg}</td> <td>${exception}</td> </tr> </s:iterator> </table> </div> </div> <div class="panel panel-info"> <div class="panel-heading"> <h2 class="text-center">Job History</h2> </div> <div class="panel-body"> <table class="table-hover table"> <tr> <th>序号</th> <th style="width: 200px;">任务名称</th> <th style="min-width: 180px;">执行时间</th> <th style="width: 100px;">耗时(ms)</th> <th style="width: 100px;">class</th> </tr> <s:iterator value="jobHistoryList" id="jobList" status="step"> <tr> <td>${step.index+1}</td> <td>${jobName}</td> <td>${startTime} - ${endTime}</td> <td>${takesTime}</td> <td>${jobDetail.jobClass}</td> </tr> </s:iterator> </table> </div> </div> </div> </body> </html>
本文介绍的只是一个简单的实现,推荐使用设计模式的一些方式进行重构,如果以后有时间我也会重构
标签:quartz schedulerfactorybean 可视化
原文地址:http://blog.csdn.net/lanmo555/article/details/45194661