Quartz概述

1、quartz导读:https://blog.csdn.net/u010648555/article/details/54863144

2、quarz核心类:

JobDetail:传递给定作业实例的详细信息属性,JobBuilder使用Builder模式构造JobDetail对象,JobDetail实现类是JobDetailImpl

JobBuilder:可以看成JobDetail的构造器,JobDetailImpl的属性name、group标识唯一性,jobClass标识job类名,jobDataMap定义job的data,可在运行时获取,durability指定JobDetail没有Trigger关联时是否存储

Job:要由表示要执行的“作业”的类实现的接口。只有一个方法 void execute(jobExecutionContext context) (jobExecutionContext 提供调度上下文各种信息,运行时数据保存在jobDataMap中) ,Job有个子接口StatefulJob ,代表有状态任务。 有状态任务不可并发,前次任务没有执行完,后面任务处于阻塞等到。

JobDataMap:存储Job和Trigger的运行时数据,其实就是Map对象

JobExecutionContext:定义Job执行时的上下文,可从中获取Job和Trigger的数据,包括上面的JobDataMap

ScheduleBuilder:时间规则的接口,实现类有SimpleScheduleBuilder,CronScheduleBuilder

SimpleScheduleBuilder:简单时间规则,包含了interval、repeatCount属性,用来构造SimpleTriggerImpl

CronScheduleBuilder:cron表达式规则,包含了cronExpression属性,用来构造CronTriggerImpl

Trigger:具有所有触发器通用属性的基本接口,描述了job执行的时间出发规则。 - 使用TriggerBuilder实例化实际触发器。 

TriggerBuilder:可以看成Trigger的构造器,其实SimpleScheduleBuilder、CronScheduleBuilder也可以看成Trigger的构造器

Scheduler:这是Quartz Scheduler的主要接口,代表一个独立运行容器。调度程序维护JobDetails和触发器的注册表。 一旦注册,调度程序负责执行作业,当他们的相关联的触发器触发(当他们的预定时间到达时),实现类是StdScheduler,这是一个代理类,核心的是QuartzScheduler

QuartzScheduler:这是Quartz的核心,它是org.quartz.Scheduler接口的间接实现,包含调度org.quartz.Jobs,注册org.quartz.JobListener实例等的方法。 

QuartzSchedulerResources:包含创建QuartzScheduler实例所需的所有资源(JobStore,ThreadPool等)。

QuartzSchedulerThread:负责执行向QuartzScheduler注册的触发Trigger的工作的线程。

ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提供运行效率。  

JobStore: 通过类实现的接口,这些类要为org.quartz.core.QuartzScheduler的使用提供一个org.quartz.Job和org.quartz.Trigger存储机制。作业和触发器的存储应该以其名称和组的组合为唯一性。 

SchedulerFactory :提供用于获取调度程序实例的客户端可用句柄的机制。 

3、编程思想

调用示例

        SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

        // 获取一个调度器
        Scheduler sched = schedFact.getScheduler();

        JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
        // 每两秒执行
        DailyTimeIntervalTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").withSchedule(
                DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule().withInterval(2, DateBuilder.IntervalUnit.SECOND)
        ).build();
        sched.scheduleJob(job, trigger);
        // 调度启动
        sched.start();

3.1、工厂模式

工厂模式用来生产产品,有两个关键的角色:产品及工厂

StdSchedulerFactory工厂实现SchedulerFactory接口,生产Scheduler产品

 public Scheduler getScheduler() throws SchedulerException {
        //判断cfg是否为空,如果为空,则还没有初始化,需要根据配置文件初始化配置
        if (cfg == null) {
            initialize();
        }
        //根据SchedulerName获取Scheduler
        SchedulerRepository schedRep = SchedulerRepository.getInstance();

        Scheduler sched = schedRep.lookup(getSchedulerName());
        //如果获取了没有销毁,则直接返回,如果销毁了直接remove掉
        if (sched != null) {
            if (sched.isShutdown()) {
                schedRep.remove(getSchedulerName());
            } else {
                return sched;
            }
        }
        //此处需要构造Scheduler实例
        sched = instantiate();

        return sched;
    }

SimpleJobFactory工厂实现JobFactory接口,生产Job产品,上面通过JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build()创建了一个JobDetail并注册到调度程序中,在运行时实际上是创建了一个HelloJob的任务运行

public class SimpleJobFactory implements JobFactory {

    private final Logger log = LoggerFactory.getLogger(getClass());
    
    protected Logger getLog() {
        return log;
    }
    
    public Job newJob(TriggerFiredBundle bundle, Scheduler Scheduler) throws SchedulerException {

        JobDetail jobDetail = bundle.getJobDetail();
        Class<? extends Job> jobClass = jobDetail.getJobClass();
        try {
            if(log.isDebugEnabled()) {
                log.debug(
                    "Producing instance of Job '" + jobDetail.getKey() + 
                    "', class=" + jobClass.getName());
            }
            
            return jobClass.newInstance();
        } catch (Exception e) {
            SchedulerException se = new SchedulerException(
                    "Problem instantiating class '"
                            + jobDetail.getJobClass().getName() + "'", e);
            throw se;
        }
    }

}

3.2、生成器模式

生成器模式相当于类的构造器,当类中有复杂的属性时可以考虑这种模式,例如quartz中Trigger,抽象类AbstractTrigger封装了trigger的共同属性,派生类SimpleTriggerImpl和CronTriggerImpl封装了trigger的触发时间规则,前者是固定时间间隔简单触发器,后者是cron表达式的复杂触发器,常规思路是根据场景调用SimpleTriggerImpl和CronTriggerImpl不同的构造方法生成对象,但这样就会有很多构造方法,而且调用很复杂。

public abstract class AbstractTrigger<T extends Trigger> implements OperableTrigger {

    private static final long serialVersionUID = -3904243490805975570L;

    private String name;

    private String group = Scheduler.DEFAULT_GROUP;

    private String jobName;

    private String jobGroup = Scheduler.DEFAULT_GROUP;

    private String description;

    private JobDataMap jobDataMap;

    private boolean volatility = false; // still here for serialization backward compatibility

    private String calendarName = null;

    private String fireInstanceId = null;

    private int misfireInstruction = MISFIRE_INSTRUCTION_SMART_POLICY;

    private int priority = DEFAULT_PRIORITY;

    private transient TriggerKey key = null;
}
public class SimpleTriggerImpl extends AbstractTrigger<SimpleTrigger> implements SimpleTrigger, CoreTrigger {

    private static final long serialVersionUID = -3735980074222850397L;

    private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100;
   
    private Date startTime = null;

    private Date endTime = null;

    private Date nextFireTime = null;

    private Date previousFireTime = null;

    private int repeatCount = 0;

    private long repeatInterval = 0;

    private int timesTriggered = 0;

    private boolean complete = false;

}
public class CronTriggerImpl extends AbstractTrigger<CronTrigger> implements CronTrigger, CoreTrigger {

    private static final long serialVersionUID = -8644953146451592766L;

    protected static final int YEAR_TO_GIVEUP_SCHEDULING_AT = CronExpression.MAX_YEAR; 

    private CronExpression cronEx = null;
    private Date startTime = null;
    private Date endTime = null;
    private Date nextFireTime = null;
    private Date previousFireTime = null;
    private transient TimeZone timeZone = null;
}

quartz在这里封装了三个构造器生成trigger对象,TriggerBuilder构造AbstractTrigger属性,抽象类ScheduleBuilder派生类SimpleScheduleBuilder和CronScheduleBuilder构造SimpleTriggerImpl和CronTriggerImpl属性

public class TriggerBuilder<T extends Trigger> {

    private TriggerKey key;
    private String description;
    private Date startTime = new Date();
    private Date endTime;
    private int priority = Trigger.DEFAULT_PRIORITY;
    private String calendarName;
    private JobKey jobKey;
    private JobDataMap jobDataMap = new JobDataMap();
    
    private ScheduleBuilder scheduleBuilder = null;
}
public class SimpleScheduleBuilder extends ScheduleBuilder<SimpleTrigger> {

    private long interval = 0;
    private int repeatCount = 0;
    private int misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_SMART_POLICY;  
}
public class CronScheduleBuilder extends ScheduleBuilder<CronTrigger> {

    private String cronExpression;
    private TimeZone tz;
    private int misfireInstruction = CronTrigger.MISFIRE_INSTRUCTION_SMART_POLICY;
}

每个xxxBuilder的方法,提供了属性设置方法,并返回对象本身,这样就可以直接在设置某个属性后直接设置其他属性,例如newTrigger() .withIdentity(triggerKey("myTrigger","myTriggerGroup")).withSchedule(simpleSchedule() .withIntervalInHours(1) .repeatForever()).startAt(futureDate(10, MINUTES)).build(),build方法生成具体的对象。这里SimpleScheduleBuilder生成SimpleTriggerImpl对象,CronScheduleBuilder生成CronTriggerImpl对象,TriggerBuilder设置父类的属性。

public class TriggerBuilder<T extends Trigger> {

 private TriggerBuilder() {
        
    }
public static TriggerBuilder<Trigger> newTrigger() {
        return new TriggerBuilder<Trigger>();
    }
public TriggerBuilder<T> withIdentity(String name) {
        key = new TriggerKey(name, null);
        return this;
    }  
public TriggerBuilder<T> withIdentity(String name, String group) {
        key = new TriggerKey(name, group);
        return this;
    }
public T build() {

        if(scheduleBuilder == null)
            scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
        MutableTrigger trig = scheduleBuilder.build();
        
        trig.setCalendarName(calendarName);
        trig.setDescription(description);
        trig.setEndTime(endTime);
        if(key == null)
            key = new TriggerKey(Key.createUniqueName(null), null);
        trig.setKey(key); 
        if(jobKey != null)
            trig.setJobKey(jobKey);
        trig.setPriority(priority);
        trig.setStartTime(startTime);
        
        if(!jobDataMap.isEmpty())
            trig.setJobDataMap(jobDataMap);
        
        return (T) trig;
    }
}

3.3、观察者模式

观察者设计模式适合多种对象跟踪一个对象数据变化的程序结构问题,有一个称作“主题”的对象和若干个“观察者”的对象,因此观察者模式涉及两个角色:主题和观察者,观察者类似于监控的角色,quartz可以注册调度程序、任务、触发器的观察者,前面讲到的Scheduler实现类StdScheduler是一个代理类,内部包含了QuartzScheduler对象,我们在调用示例中使用sched.scheduleJob(job, trigger)向调度程序注册了任务和触发器,现在我们想监控调度程序任务运行的情况,有什么办法吗,答案是有的,我们先看看QuartzScheduler属性

public class QuartzScheduler implements RemotableQuartzScheduler {
    private QuartzSchedulerResources resources;

    private QuartzSchedulerThread schedThread;

    private ThreadGroup threadGroup;

    private SchedulerContext context = new SchedulerContext();

    private ListenerManager listenerManager = new ListenerManagerImpl();
    
    private HashMap<String, JobListener> internalJobListeners = new HashMap<String, JobListener>(10);

    private HashMap<String, TriggerListener> internalTriggerListeners = new HashMap<String, TriggerListener>(10);

    private ArrayList<SchedulerListener> internalSchedulerListeners = new ArrayList<SchedulerListener>(10);

    private JobFactory jobFactory = new SimpleJobFactory();
    
    ExecutingJobsManager jobMgr = null;

    ErrorLogger errLogger = null;

    private SchedulerSignaler signaler;

    private Random random = new Random();

    private ArrayList<Object> holdToPreventGC = new ArrayList<Object>(5);

    private boolean signalOnSchedulingChange = true;

    private volatile boolean closed = false;
    private volatile boolean shuttingDown = false;
    private boolean boundRemotely = false;

    private QuartzSchedulerMBean jmxBean = null;
    
    private Date initialStart = null;
    
    private final Timer updateTimer;

    private final Logger log = LoggerFactory.getLogger(getClass());
}

上面类中listenerManager的属性正是我们解决的办法,那么怎么注册这些观察者呢,看看ListenerManager,提供了JobListener、TriggerListene、SchedulerListener添加

public interface ListenerManager {

    public void addJobListener(JobListener jobListener, Matcher<JobKey> ... matchers);

    public void addJobListener(JobListener jobListener, List<Matcher<JobKey>> matchers);

    public boolean addJobListenerMatcher(String listenerName, Matcher<JobKey> matcher);

    public boolean removeJobListenerMatcher(String listenerName, Matcher<JobKey> matcher);

    public boolean setJobListenerMatchers(String listenerName, List<Matcher<JobKey>> matchers);

    public List<Matcher<JobKey>> getJobListenerMatchers(String listenerName);

    public boolean removeJobListener(String name);

    public List<JobListener> getJobListeners();

    public JobListener getJobListener(String name);

    public void addTriggerListener(TriggerListener triggerListener, Matcher<TriggerKey> ... matchers);

    public void addTriggerListener(TriggerListener triggerListener, List<Matcher<TriggerKey>> matchers);

    public boolean addTriggerListenerMatcher(String listenerName, Matcher<TriggerKey> matcher);

    public boolean removeTriggerListenerMatcher(String listenerName, Matcher<TriggerKey> matcher);

    public boolean setTriggerListenerMatchers(String listenerName, List<Matcher<TriggerKey>> matchers);

    public List<Matcher<TriggerKey>> getTriggerListenerMatchers( String listenerName);

    public boolean removeTriggerListener(String name);

    public List<TriggerListener> getTriggerListeners();

    public TriggerListener getTriggerListener(String name);

    public void addSchedulerListener(SchedulerListener schedulerListener);

    public boolean removeSchedulerListener(SchedulerListener schedulerListener);

    public List<SchedulerListener> getSchedulerListeners();

}

那么何时注册这些观察者呢,quartz在StdSchedulerFactory工厂类的initialize方法中根据配置文件来注册,程序中开发了JobListener、TriggerListener,可以再quartz.properties中添加,下面看看StdSchedulerFactory如何注册的,就是根据配置文件生成JobListener、TriggerListener对象,再调用QuartzScheduler的listenerManager注册,目前没有实现SchedulerListener添加

        String[] jobListenerNames = cfg.getPropertyGroups(PROP_JOB_LISTENER_PREFIX);
        JobListener[] jobListeners = new JobListener[jobListenerNames.length];
        for (int i = 0; i < jobListenerNames.length; i++) {
            Properties lp = cfg.getPropertyGroup(PROP_JOB_LISTENER_PREFIX + "."
                    + jobListenerNames[i], true);

            String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);

            if (listenerClass == null) {
                initException = new SchedulerException(
                        "JobListener class not specified for listener '"
                                + jobListenerNames[i] + "'");
                throw initException;
            }
            JobListener listener = null;
            try {
                listener = (JobListener)
                       loadHelper.loadClass(listenerClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobListener class '" + listenerClass
                                + "' could not be instantiated.", e);
                throw initException;
            }
            try {
                Method nameSetter = listener.getClass().getMethod("setName", strArg);
                if(nameSetter != null) {
                    nameSetter.invoke(listener, new Object[] {jobListenerNames[i] } );
                }
                setBeanProps(listener, lp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobListener '" + listenerClass
                                + "' props could not be configured.", e);
                throw initException;
            }
            jobListeners[i] = listener;
        }
        String[] triggerListenerNames = cfg.getPropertyGroups(PROP_TRIGGER_LISTENER_PREFIX);
        TriggerListener[] triggerListeners = new TriggerListener[triggerListenerNames.length];
        for (int i = 0; i < triggerListenerNames.length; i++) {
            Properties lp = cfg.getPropertyGroup(PROP_TRIGGER_LISTENER_PREFIX + "."
                    + triggerListenerNames[i], true);

            String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);

            if (listenerClass == null) {
                initException = new SchedulerException(
                        "TriggerListener class not specified for listener '"
                                + triggerListenerNames[i] + "'");
                throw initException;
            }
            TriggerListener listener = null;
            try {
                listener = (TriggerListener)
                       loadHelper.loadClass(listenerClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "TriggerListener class '" + listenerClass
                                + "' could not be instantiated.", e);
                throw initException;
            }
            try {
                Method nameSetter = listener.getClass().getMethod("setName", strArg);
                if(nameSetter != null) {
                    nameSetter.invoke(listener, new Object[] {triggerListenerNames[i] } );
                }
                setBeanProps(listener, lp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "TriggerListener '" + listenerClass
                                + "' props could not be configured.", e);
                throw initException;
            }
            triggerListeners[i] = listener;
        }
            for (int i = 0; i < jobListeners.length; i++) {
                qs.getListenerManager().addJobListener(jobListeners[i], EverythingMatcher.allJobs());
            }
            for (int i = 0; i < triggerListeners.length; i++) {
                qs.getListenerManager().addTriggerListener(triggerListeners[i], EverythingMatcher.allTriggers());
            }

那么何时通知这些观察者呢,通过下面的代码我们可以看到这些观察者主要监控任务的执行,即任务执行时

public interface TriggerListener {

    String getName();

    void triggerFired(Trigger trigger, JobExecutionContext context);

    
    boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);

    
    void triggerMisfired(Trigger trigger);

    void triggerComplete(Trigger trigger, JobExecutionContext context,
            CompletedExecutionInstruction triggerInstructionCode);

}
public interface JobListener {

    String getName();

    void jobToBeExecuted(JobExecutionContext context);

    void jobExecutionVetoed(JobExecutionContext context);

    void jobWasExecuted(JobExecutionContext context,
            JobExecutionException jobException);

}
public class JobRunShell extends SchedulerListenerSupport implements Runnable {
public void run() {
        qs.addInternalSchedulerListener(this);

        try {
            OperableTrigger trigger = (OperableTrigger) jec.getTrigger();
            JobDetail jobDetail = jec.getJobDetail();

            do {

                JobExecutionException jobExEx = null;
                Job job = jec.getJobInstance();

                try {
                    begin();
                } catch (SchedulerException se) {
                    qs.notifySchedulerListenersError("Error executing Job ("
                            + jec.getJobDetail().getKey()
                            + ": couldn't begin execution.", se);
                    break;
                }

                // notify job & trigger listeners...
                try {
                    if (!notifyListenersBeginning(jec)) {
                        break;
                    }
                } catch(VetoedException ve) {
                    try {
                        CompletedExecutionInstruction instCode = trigger.executionComplete(jec, null);
                        try {
                            qs.notifyJobStoreJobVetoed(trigger, jobDetail, instCode);
                        } catch(JobPersistenceException jpe) {
                            vetoedJobRetryLoop(trigger, jobDetail, instCode);
                        }
                        complete(true);
                    } catch (SchedulerException se) {
                        qs.notifySchedulerListenersError("Error during veto of Job ("
                                + jec.getJobDetail().getKey()
                                + ": couldn't finalize execution.", se);
                    }
                    break;
                }

                long startTime = System.currentTimeMillis();
                long endTime = startTime;

                // execute the job
                try {
                    log.debug("Calling execute on job " + jobDetail.getKey());
                    job.execute(jec);
                    endTime = System.currentTimeMillis();
                } catch (JobExecutionException jee) {
                    endTime = System.currentTimeMillis();
                    jobExEx = jee;
                    getLog().info("Job " + jobDetail.getKey() +
                            " threw a JobExecutionException: ", jobExEx);
                } catch (Throwable e) {
                    endTime = System.currentTimeMillis();
                    getLog().error("Job " + jobDetail.getKey() +
                            " threw an unhandled Exception: ", e);
                    SchedulerException se = new SchedulerException(
                            "Job threw an unhandled exception.", e);
                    qs.notifySchedulerListenersError("Job ("
                            + jec.getJobDetail().getKey()
                            + " threw an exception.", se);
                    jobExEx = new JobExecutionException(se, false);
                }

                jec.setJobRunTime(endTime - startTime);

                // notify all job listeners
                if (!notifyJobListenersComplete(jec, jobExEx)) {
                    break;
                }

                CompletedExecutionInstruction instCode = CompletedExecutionInstruction.NOOP;

                // update the trigger
                try {
                    instCode = trigger.executionComplete(jec, jobExEx);
                } catch (Exception e) {
                    // If this happens, there's a bug in the trigger...
                    SchedulerException se = new SchedulerException(
                            "Trigger threw an unhandled exception.", e);
                    qs.notifySchedulerListenersError(
                            "Please report this error to the Quartz developers.",
                            se);
                }

                // notify all trigger listeners
                if (!notifyTriggerListenersComplete(jec, instCode)) {
                    break;
                }

                // update job/trigger or re-execute job
                if (instCode == CompletedExecutionInstruction.RE_EXECUTE_JOB) {
                    jec.incrementRefireCount();
                    try {
                        complete(false);
                    } catch (SchedulerException se) {
                        qs.notifySchedulerListenersError("Error executing Job ("
                                + jec.getJobDetail().getKey()
                                + ": couldn't finalize execution.", se);
                    }
                    continue;
                }

                try {
                    complete(true);
                } catch (SchedulerException se) {
                    qs.notifySchedulerListenersError("Error executing Job ("
                            + jec.getJobDetail().getKey()
                            + ": couldn't finalize execution.", se);
                    continue;
                }

                try {
                    qs.notifyJobStoreJobComplete(trigger, jobDetail, instCode);
                } catch (JobPersistenceException jpe) {
                    qs.notifySchedulerListenersError(
                            "An error occured while marking executed job complete. job= '"
                                    + jobDetail.getKey() + "'", jpe);
                    if (!completeTriggerRetryLoop(trigger, jobDetail, instCode)) {
                        return;
                    }
                }

                break;
            } while (true);

        } finally {
            qs.removeInternalSchedulerListener(this);
        }
    }
}

猜你喜欢

转载自my.oschina.net/u/3769440/blog/1934114
今日推荐