6. 创建 RMI 客户端
你需要创建一个客户端,用来调用远程 Quartz 调度器上的方法。客户端会同 RMI 注册服务器进行通信,进而定位到远程调度器对象,然后就能够调用其上的方法了。这些方法包括有暂停和停止调度器、部署和卸下 Job,和执行所有其他对与远程客户端可见的方法。
·配置 Quartz RMI 客户端
类似于表 9.1 所示服务端的配置,表 9.2 所列出的属性也是必须加到 Quartz RMI 客户端的。这两份属性列表必须分别应用到服务端和客户端的。
属性 | 默认值 |
org.quartz.scheduler.rmi.registryHost 注:这是运行 RMI 注册服务所在的主机 |
localhost |
org.quartz.scheduler.rmi.registryPort 注:这是运行 RMI 注册服务所监听的端口(通常是 1099) |
1099 |
org.quartz.scheduler.rmi.proxy 注:假如你希望连接到远程服务端的调度器,设置 org.quartz.scheduler.rmi.proxy 标志为 true。你同时必须指定 RMI 注册服务进程的主机和端口号。 |
false |
为了能让客户端定位到服务对象,它需要知道 RMI 注册服务运行在哪里,以便能查找到远程对象。org.quartz.scheduler.rmi.registryHost 和 org.quartz.scheduler.rmi.registryPort 属性必须是运行着 RMI 注册服务的主机和端口。假如你配置了 Quartz RMI 服务端自动启动注册服务,那么 RMI 注册服务器与 RMI 服务端就是同在一个机器上的。
因为你想要客户端能联系到远程调度器去部署 Job,你必须设置属性 org.quartz.scheduler.rmi.proxy 为 true。
代码 9.3 就是一个 RMI 客户端用来与服务器进行通信的 quartz.properties 示例文件
代码 9.3 一个用于 Quartz RMI 客户端的 quartz.properties 文件例子
1 2 3 4 5 6 7 8 9 10 11 12 |
#============================================================= # Configure Main Scheduler Properties #============================================================= org.quartz.scheduler.instanceName = RMIScheduler #org.quartz.scheduler.instanceId = AUTO #============================================================== #Configure RMI Properties #============================================================== org.quartz.scheduler.rmi.registryHost=localhost org.quartz.scheduler.rmi.registryPort=1099 org.quartz.scheduler.rmi.proxy= true |
除了上面所说的三个 RMI 属性之外,你曾经见过像代码 9.3 所示的 quartz.properties 属性文件。
客户端和服务器的实例名必须相匹配 属性 org.quartz.scheduler.instanceName 在 RMI 客户端和服务端必须一致。不然,客户将无法在注册服务中查找到服务对象,会收一个客户端无法获取到远程调度器句柄的异常。 |
·创建 RMI 客户端类
在为客户端配置好了属性文件之后,你需要构建一个客户端 Java 类,去获取指向远程调度器的句柄并据此做点什么。创建这个类并不用我们做太多的事情,正如创建服务端类那般,我们把 quartz.properties 文件更名为 client.properties 并且告诉客户端从更名后的文件中加载属性。同样的,这样做的目的只是有助于我们清楚的知道是加载的哪一个属性文件以免产生混乱。除了这点这改变之外,你已经是多次的在前面章节中看到像代码 9.4 那样的配置了。
代码 9.4. 通过远程调度器部署一个 Job 的 Quartz RMI 客户端的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
package org.cavaness.quartzbook.chapter9; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.CronTrigger; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.impl.StdSchedulerFactory; public class RMITestClient { public void run() throws Exception { Log log = LogFactory.getLog(RMITestClient.class); // Use this properties file instead of quartz.properties System.setProperty("org.quartz.properties", "client.properties"); // Get a reference to the remote scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // Define the job to add JobDetail job = new JobDetail("remotelyAddedJob", "default", SimpleJob.class); JobDataMap map = new JobDataMap(); map.put("msg", "Your remotely added job has executed!"); job.setJobDataMap(map); CronTrigger trigger = new CronTrigger("remotelyAddedTrigger", "default", "remotelyAddedJob", "default", new Date(), null, "/5 * * ? * *"); // schedule the remote job scheduler.scheduleJob(job, trigger); log.info("Remote job scheduled."); } public static void main(String[] args) throws Exception { RMITestClient example = new RMITestClient(); example.run(); } } |
我们观察到一件有趣的事,也近乎魔术般的就是根本不用告诉工厂类我们想要的是一个远程调度器。工厂类是依据我们告诉它所加载的 client.properties 文件知道这么做的。明确的讲就是,设置了 RMI 属性导引着工厂类创建了一个远程调度器:
org.quartz.scheduler.rmi.proxy = true
下面是 StdSchedulerFactory 的一个代码片断,它们决定了客户端去连接到一个远程调度器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
if (rmiProxy) { if (autoId) schedInstId = DEFAULT_INSTANCE_ID; schedCtxt = new SchedulingContext(); schedCtxt.setInstanceId(schedInstId); String uid = QuartzSchedulerResources.getUniqueIdentifier( schedName, schedInstId); RemoteScheduler remoteScheduler = new RemoteScheduler(schedCtxt, uid, rmiHost, rmiPort); schedRep.bind(remoteScheduler); return remoteScheduler; } |
以上代码出现在 StdSchedulerFactory 的 instantiate() 方法中。在这段代码中,工厂类检查为客户端设置的 rmiProxy 是否为 true。如果为 true,一个新的 RemoteScheduler 将被初始化并返回。这就是为什么我们的客户端并不需做任何特别的事情就行。返回到我们客户端的调度器实例实质上就是一个远程调度器(RemoteScheduler) 实例,因为 RemoteScheduler 实现了 Scheduler 接口,所以客户端代码可以对此毫不知情。
7. 测试 RMI 服务端和客户端
我们最后终于来到了可以运行客户端和服务端去测试 RMI 配置的时刻了。第一件要做的事情是启动 Quartz RMI 服务端。除了确保基本的 Quartz JAR 包和 server.properties 文件包含在 classpath 下之外,不用去做其他特别的事情了。运行 RMI 用不着附加的 JAR 文件,只是需要运行任何一个 Quartz 程序所必须的包而已。
·运行 Quartz RMI 服务端
要运行 Quartz RMI 服务端,仅仅是像运行别的 Java 类一样运行 QuartzRMIServer 类。就在前面提到过,要确保 classpath 下包含了所需的 JAR 文件和 server.properties 文件。要完成这个,最简单的方式就是创建一个批处理文件(或shell 脚本),把需要的东西写在其中。代码 9.4 就是一个用来启动服务端的简单的批处理文件。
代码 9.4. 一个简单的用来启动 Quartz RMI 服务端的 startserver.bat 批处理文件
java org.cavaness.quartzbook.chapter9.QuartzRMIServer
你还需要在代码9.4 的命令行中包含所需的 JAR 文件到 classpath 上才能正常工作。这包括有 quartz.jar、commons-logging.jar、commons-logging-api.jar、commons-collections3.1.jar、beanutils.jar、commons-beanutils-bean-collections.jar、commons-beanutils-core.jar。·运行 Quartz RMI 客户端
当服务端运行起来之后,你就可以运行 RMI 客户端了。客户端能以与服务端同样的方式运行--创建一个批处理文件或 shell 脚本。当启动客户端后,假如一切正常的话,你将不仅仅能看到从客户端控制台输出了一个远程 Job 被部署了的消息,还能看到在服务端控制台打印出一条消息,是说接收到了远程 Job 并部署运行。
尽管这个 RMI 客户端被设计为部署一个 Job 后就退出,Quartz RMI 服务端被设计为持续运行直到键入 exit 为止。服务端可以写成是接收到一个远程 Job,进行部署,然后执行自己的逻辑去等待更多的客户连接。这就意味着你能多次的运行客户端,像这样的设计应该才是你想要的。
当你完成了运行这个例子之后,只要键入 exit 即可。
·接下来是什么?
本章介绍了与 Quartz 调度器打交道的一种新的方式,这在之前是不可能的。在 Quartz 中使用 RMI,允许你构建组件分离的程序并能分布在跨平台的机器中。这是一个非常好的概念,因为它在没有增加额外开发工作量的情况下提供了更好的可伸缩性。
本文链接 https://yanbin.blog/quartz-job-scheduling-framework-9-3/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
翻译的不错呀,怎么没有下文了,要加把劲呀
唉,无奈最近比较忙,周六都得坐在办公室,我会再接再励的。
@隔叶黄莺
我很佩服你。。。哥们。。有空来坐坐