部署 Job (第一部分)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Shirley_John_Thomas/article/details/55260425

在上一章中,你首次尝试使用了 Quartz 来部署 Job。无可否认地,那些 Job 都不是很复杂,但这个不是重点。你应该轻松的对如何构造并部署 Job 有了相当的了解,更重要的是,由此热情的希望学得更多的东西。在本章中将会继续给你讲述。

第四章将带领你深入到 Quart 框架的核心部分。可证明的是,这一章对于阅读和理解本书是非常之重要的。调度器(Scheduler) 是此框架的心脏。本章关注于如何使用 Scheduler 来管理你的 Job;如何创建并关联触发器以使 Job 能被触发执行;以及如可选择 calendar 为给定的时程安排提供更多的灵活性。

此刻,什么也没发生,下一刻,仍旧什么也没发生。

道格拉斯.亚当斯,《银河系漫游指南》

1. Quartz 调度器

Quartz 框架包含许多的类和接口,它们分布在大概 11 个包中。多数你所要使用到的类或接口放置在 org.quartz 包中。这个包含盖了 Quartz 框架的公有 API.

我们不打算对这个框架的所有类和接口都面面俱到。而所要介绍的是那些有助于你理解 Quartz 如何做它该做事情的组件的子集。图 4.1 展示了一个只留下必要的调度器的精简类图。

Quartz 类图(仅显示主要组件)


Scheduler 是 Quartz 的主要 API。对于 Quartz 用户来说,多数时候与框架的交互是发生于 Scheduler  之上的。客服端与 Scheduler 交互是通过 org.quartz.Scheduler 接口的。这个 Scheduler 的实现,在这种情况下,是一个代理,对其中方法调用会传递到 QuartzScheduler 实例上。QuartzScheduler 对于客户端是不可见的,并且也不存在与此实例的直接交互。

QuartzScheduler 处在框架根的位置,它是一个引擎驱动着整个框架。并非所有的功能都直接内建到 QuartzScheduler,然而,框架为灵活性和可配置性考虑而设计,所以许多重要的功能由分离的组件和子框架实现。这就意味着 Quartz 用户可以用自己某个关键特征实现来替换原有默认实现。即使 QuartzScheduler 代理了它的一些职责,但它仍然掌控着整个作业调度流程。

·Quartz Scheduler 类层次

客户端会同两种类型的 Scheduler 交互,如图 4.2. 它们都实现了 org.quartz.Scheduler 接口。

org.quartz.Scheduler 类层次


作为一个 Quartz 用户,你要与实现了 org.quartz.Scheduler 接口的类交互。在你调用它的任何 API 之前,你需要知道如何创建一个 Scheduler 的实例。

2. Quartz SchedulerFactory

尽管你已使用到了 Scheduler 类型了,但你未曾显式的去创建 Scheduler 的实例。取而代之的是用了某个工厂方法来确保了构造出 Sheduler 实例并正确的得到初始化。(工厂设计模式之所以谓之工厂模式是因为它承担了生产对象的职责。在这里是生产了一个 Scheduler 实例) Quartz 框架为这一目的提供了 org.quartz.SchedulerFactory 接口。角色 SchedulerFactory 就是用来产生 Scheduler 实例的。当 Scheduler 实例被创建之后,就会存到一个仓库中(org.quartz.impl.SchedulerRepository),这个仓库还提供了通过一个 class loader 查询实例的机制。要使用 Scheduler 实例,客户端必须从工厂(和随同的仓库中)使用不同方法调用来获取到它们。换句话说,要通过工厂创建一个 Scheduler 实例并获取到它需要经由两次方法调用。有一些方便的方法封装了那两个方法,你将很快能看到。

你可使用两种不同类型的 SchedulerFactory 来创建 Scheduler 实例 (看图 4.3)

所有的 Scheduler 实例应该由 SchedulerFactory 来创建


这两个 Scheduler 工厂分别是 org.quartz.impl.DirectoSchedulerFactory 和 org.quartz.impl.StdSchedulerFactory. 让我们来逐个检视它们。

·使用 DirectSchedulerFactory

DirectSchedulerFactory 是为那些想绝对控制 Scheduler 实例是如何生产出的人所设计的。代码 4.1 显示了最简单的方式去使用 DirectSchedulerFactory 来创建一个 Scheduler 实例。

代码 4.1. 使用 DirectSchedulerFactory

  1. public class Listing_4_1 {  
  2. static Log logger = LogFactory.getLog(Listing_4_1.class);  
  3. public static void main(String[] args) {  
  4.           Listing_4_1 example = new Listing_4_1();  
  5.           example.startScheduler();  
  6.      }  
  7. public void startScheduler() {  
  8.           DirectSchedulerFactory factory=DirectSchedulerFactory.getInstance();  
  9. try {  
  10. // Initialize the Scheduler Factory with 10 threads
  11.               factory.createVolatileScheduler(10);  
  12. // Get a scheduler from the factory
  13.               Scheduler scheduler = factory.getScheduler();  
  14. // Start the scheduler running
  15.               logger.info("Scheduler starting up...");  
  16.               scheduler.start();  
  17.           } catch (SchedulerException ex) {  
  18.                logger.error(ex);  
  19.           }  
  20.      }  

public class Listing_4_1 { static Log logger = LogFactory.getLog(Listing_4_1.class); public static void main(String[] args) { Listing_4_1 example = new Listing_4_1(); example.startScheduler(); } public void startScheduler() { DirectSchedulerFactory factory=DirectSchedulerFactory.getInstance(); try { // Initialize the Scheduler Factory with 10 threads factory.createVolatileScheduler(10); // Get a scheduler from the factory Scheduler scheduler = factory.getScheduler(); // Start the scheduler running logger.info("Scheduler starting up..."); scheduler.start(); } catch (SchedulerException ex) { logger.error(ex); } } }

当使用 DirectSchedulerFactory 时,有三个基本的步骤。首先,你必须用静态方法 getInstance() 获取到工厂的实例。当你持有了工厂的实例之后,你必须调用其中的一个 createXXX 方法去初始化它。如代码 4.1 所示例子中是调用 createVolatileScheduler() 方法告诉工厂以十个工作者线程初始化它自己(至于工作者线程的更多内容将在本章的后部分讨论到)。第三步也就是最后一步是通过工厂的 getScheduler() 方法拿到 Scheduler 的实例。

在调用 getScheduler() 方法之后调用其中的一个 createXXX 方法
方法 createVolatileScheduler() 方法不会返回 scheduler 的实例。createXXX() 方法是告诉工厂如何配置要创建的 Scheduler 实例。你必须调用方法 getScheduler() 获取到在工厂上执行方法 createXXX() 产生的实例。实际上,在调用 getScheduler() 方法之前,你必须调用其中一个 createXXX() 方法;否则,你将有收到一个 SchedulerException 错误,因为根本没有 Scheduler 实例存在。

你可从数个不同的 createXXX() 方法中选择,依赖于你想要的 Scheduler 类型和你需要怎样的配置。代码 4.1 中用的是 createVolatileScheduler() 方法创建 Scheduler 实例的。方法 createVolatileScheduler() 带有单个参数:要创建的线程数量。在图 4.2 中,你已看到还有一个 RemoteScheduler 类。你必须要用一个不同的 createXXX() 方法去创建 RemoteScheduler 实例。有两个版本的方法可用:

  1. public void createRemoteScheduler(String rmiHost, int rmiPort)  
  2. throws SchedulerException;  
  3. protected void createRemoteScheduler(String schedulerName,  
  4.   String schedulerInstanceId, String rmiHost, int rmiPort)  
  5. throws SchedulerException; 

public void createRemoteScheduler(String rmiHost, int rmiPort) throws SchedulerException; protected void createRemoteScheduler(String schedulerName, String schedulerInstanceId, String rmiHost, int rmiPort) throws SchedulerException;

RemoteScheduler 会在第十章,“J2EE 中使用 Quartz”讨论。假如你就是想要一个标准的 Scheduler, 可以调用以下三个版本的方法中的一个:

  1. public void createScheduler(ThreadPool threadPool, JobStore jobStore)  
  2. throws SchedulerException;  
  3. public void createScheduler(String schedulerName,  
  4.   String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore)  
  5. throws SchedulerException;  
  6. public void createScheduler(String schedulerName,  
  7.   String schedulerInstanceId, ThreadPool threadPool,  
  8.   JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort,  
  9. long idleWaitTime, long dbFailureRetryInterval)  
  10. throws SchedulerException; 

public void createScheduler(ThreadPool threadPool, JobStore jobStore) throws SchedulerException; public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore) throws SchedulerException; public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort, long idleWaitTime, long dbFailureRetryInterval) throws SchedulerException;

在上一章上,我们用了一个属性文件来初始化 Scheduler。要通过 DirectSchedulerFactory 创建一个 Scheduler 实例,你必须传递配置参数给其中的一个 createXXX() 方法。在下一节中,我们讨论 StdSchedulerFactory,一个 SchedulerFactory 版本,它依赖于一系列的属性来配置 Scheduler,而不是通过 createXXX() 方法参数来传递配置参数。这样也避免了在代码中对 Scheduler 的配置选项的硬编码。

·使用 StdSchedulerFactory

与 DirectSchedulerFactory 形成鲜明对比的是,org.quartz.impl.StdSchedulerFactory 依赖于一系列的属性来决定如何生产出 Scheduler 实例。你可以通过以下三种途径向工厂提供那些属性:

    ·通过 java.util.Properties 实例提供
    ·通过外部属性文件提供
    ·通过含用属性文件内容的 java.io.InputStream 实例提供

Java 属性文件
我们这里使用述语“属性文件”,对于 Java 传统来说就是:在一个外部文件中指定一系列的 key=value  对,并且每个 key=value 对独占一行。

代码 4.2 演示了第一种途径,通过一个 java.util.Properties 实例来提供属性。

代码 4.2. 使用 java.util.Properties 实例创建 StdSchedulerFactory

  1. public class Listing_4_2 {  
  2. static Log logger = LogFactory.getLog(Listing_4_2.class);  
  3. public static void main(String[] args) {  
  4.           Listing_4_2 example = new Listing_4_2();  
  5.           example.startScheduler();  
  6.      }  
  7. public void startScheduler() {  
  8. // Create an instance of the factory
  9.           StdSchedulerFactory factory = new StdSchedulerFactory();  
  10. // Create the properties to configure the factory
  11.           Properties props = new Properties();  
  12. // required to supply threadpool class and num of threads
  13.           props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,  
  14. "org.quartz.simpl.SimpleThreadPool");  
  15.           props.put("org.quartz.threadPool.threadCount", "10");  
  16. try {  
  17. // Initialize the factory with properties
  18.               factory.initialize(props);  
  19.               Scheduler scheduler = factory.getScheduler();  
  20.               logger.info("Scheduler starting up...");  
  21.               scheduler.start();  
  22.          } catch (SchedulerException ex) {  
  23.               logger.error(ex);  
  24.          }  
  25.     }  
  26. }  

public class Listing_4_2 { static Log logger = LogFactory.getLog(Listing_4_2.class); public static void main(String[] args) { Listing_4_2 example = new Listing_4_2(); example.startScheduler(); } public void startScheduler() { // Create an instance of the factory StdSchedulerFactory factory = new StdSchedulerFactory(); // Create the properties to configure the factory Properties props = new Properties(); // required to supply threadpool class and num of threads props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool"); props.put("org.quartz.threadPool.threadCount", "10"); try { // Initialize the factory with properties factory.initialize(props); Scheduler scheduler = factory.getScheduler(); logger.info("Scheduler starting up..."); scheduler.start(); } catch (SchedulerException ex) { logger.error(ex); } } }
代码 4.2 提供了一个使用 StdSchedulerFactory 创建 Scheduler 实例的很简单的例子。在这个例子中向工厂传递了两个属性,它们分别是实现了 org.quartz.spi.ThreadPool 接口的类名和 Scheduler 用来处理 Job 的线程的数量。这两个属性是必须的,因为它们并没有被指定默认值(我们很快在后面讨论它们)
在代码 4.1 中的 DirectSchedulerFactory,我们调用它其中一个 createXXX() 方法来初始化工厂。而对于 StdSchedulerFactory, 你使用的是一个有效的 initialize() 方法。在工厂初始化之后,你就可以调用它的 getScheduler() 方法获取到 Scheduler 的实例。使用 java.util.Properties 对象传递属性给工厂是一种配置 SchedulerFactory 的方式之一。硬编码配置属性的做法基本不推荐,也是可能的时候应尽力避免的做法。假如你需要修改上一例子中的线程数量,你将不得不修改代码然后重新编译。

幸运的是,StdSchedulerFactory 还有其他的方式来提供必须的属性。工厂也能通过传入一个外部文件名而被初始化,在这个外部文件中包含了这些配置项。应使用 initialize() 的替代方法形式如下:

  1. public void initialize(String filename) throws SchedulerException; 

public void initialize(String filename) throws SchedulerException;

要使文件和属性能被成功加载的话,这个文件必须对于 classloader 是可见的。也就是说它必须在你的应用程序的 classpath 中。假如你用的是 java.io.InputStream 去加载文件,你可以使用另一个 initialize() 的替代方法如下:

  1. public void initialize(InputStream propertiesStream) throws SchedulerException; 

public void initialize(InputStream propertiesStream) throws SchedulerException;
在第三章,“Hello, Quartz” 你已看到为 SchedulerFactory 从一个叫做 quartz.properties 的外部文件中加载设置的例子。这个外部属性文件就是要用前面的方法来加载。假如你没有为 initialize() 方法指定从哪儿读取属性,那么 StdSchedulerFactory 会试图从名为 quartz.properties 的文件中加载它们。这就是你在第三章看到的行为。

·使用默认的 quartz.properties 文件创建 Scheduler

假如你使用无参的 initialize() 方法,StdSchedulerFactory 会执行以下几个步骤去尝试为工厂加载属性:

1.  检查 System.getProperty("org.quartz.properties") 中是否设置了别的文件名
2.  否则,使用 quartz.properties 作为要加载的文件名
3.  试图从当前工作目录中加载这个文件
4.  试图从系统 classpath 下加载这个文件

在 Quartz Jar 包中的默认 quartz.properties 文件
上面第4步总是能成功的,因为在 Quartz Jar 包中有一个默认的 quartz.properties 文件。假如你想使用另一个替代文件,你必须自己创建一个并确保它在 classpath 上。

使用 StdSchedulerFactory 来创建 Scheduler 实例的方式很普遍,因此在 StdSchedulerFactory 直接提供了一个方便的静态方法 getDefaultScheduler(),它就是使用前面列出的几个步骤来初始化工厂的。这如代码 4.3 所示。

代码 4.3. 使用静态的 getDefaultScheduler() 方法创建 Scheduler

  1. public class Listing_4_3 {  
  2. static Log logger = LogFactory.getLog(Listing_4_3.class);  
  3. public static void main(String[] args) {  
  4.           Listing_4_3 example = new Listing_4_3();  
  5.           example.startScheduler();  
  6.      }  
  7. public void startScheduler() {  
  8. try {  
  9. // Create a default instance of the Scheduler
  10.               Scheduler scheduler =  
  11.                          StdSchedulerFactory.getDefaultScheduler();  
  12.               logger.info("Scheduler starting up...");  
  13.               scheduler.start();  
  14.           } catch (SchedulerException ex) {  
  15.                logger.error(ex);  
  16.           }  
  17.      }} 

public class Listing_4_3 { static Log logger = LogFactory.getLog(Listing_4_3.class); public static void main(String[] args) { Listing_4_3 example = new Listing_4_3(); example.startScheduler(); } public void startScheduler() { try { // Create a default instance of the Scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); logger.info("Scheduler starting up..."); scheduler.start(); } catch (SchedulerException ex) { logger.error(ex); } }}

在静态方法 getDefaultScheduler() 方法中调用了空的构造方法。假如之前未调用过任何一个 initialize() 方法,那么无参的 initialize() 方法会被调用。这会开始去按照前面说的顺序加载文件。默认情况下,quartz.properties 会被定位到,并从中加载属性。

·Scheduler 的功能

本章到此为止大部分笔墨都在着重论述如何获得一个 Scheduler 的实例。那么一旦你拿到 Scheduler 的实例之后,你能能此做些什么呢?好,现在开始,上面的例子中告诉了你可以调用它的 start() 方法。Scheduler 的 API 大概包括了 65 个不同的方法。我们不在此全部枚举出来,但是你需要理解其中的一小部分 API。

Scheduler 的 API 可以分组成以下三个类别:

    ·管理 Scheduler
    ·管理 Job
    ·管理 Trigger 和 Calendar

猜你喜欢

转载自blog.csdn.net/Shirley_John_Thomas/article/details/55260425