.jpg)
开头还是那句话,懒人直接下源码,如果有疑问,请看帖子最后的总结。
懒人兄弟们,心情不好,只打个招呼吧。
本范例演示quartz在client/server环境,利用rmi(远程方法调用)在远程服务器端安排job。
本例中首先运行服务器端以启动排程,但是不安排任何任务。客户端程序将使用RMI连接远程的服务器端,并且安排job。
一旦安排好job,服务器端排程将在正确的时间运行任务。(译者注:任务什么时候运行依赖于触发器)
友情提示:本范例运行时client端和server端最好在不同的计算机上。但是也可以运行在同一台计算机上。
本范例已经由译者修改,官方范例client.properties和server.properties的指定依赖于批处理。
但是修改后的范例不需要,首先运行服务器端程序,然后运行客户程序,最后得到需要的结果。
另外运行服务器端程序前,必须运行rmi注册表服务(至于为什么,最后讨论)。但server.properties的配置org.quartz.scheduler.rmi.createRegistry = true,使得上述步骤自动完成。
在原文中多次出现box这个单词,windows box/linux box。我的理解如下:A windows box is a computer that uses the Microsoft Windows operating system。
官方文档的已编译范例的运行(读者可以省略)
====================
1. 正确配置必须的client.properties文件和server.properties文件(详细见下面的配置部分)
2. windows用户 - 修改Modify server.bat和client.bat重新设置JAVA_HOME。先运行server.bat,启动后运行client.bat(注意:两个程序可在可不在同一box内)
3. UNIX/Linux 修改server.sh和client.sh重新设置JAVA_HOME。运行同上。
配置:
==============
1. 可以使用log4j.properties替代log4j.xml来控制日志输出(可选)
2. server.properties被用来配置服务器主机和端口以监听RMI的请求。
本例中主机是localhost端口是1099:
org.quartz.scheduler.rmi.registryHost = localhost
org.quartz.scheduler.rmi.registryPort = 1099
3. client.properties配置客户端,告诉客户端程序如何来连接远程服务器和端口。
如果服务器程序和客户程序在同一个box内,主机可以是localhost。如果两端程序运行在不同box,则你必须对registryHost属性明确说明主机名或者IP地址。
这些将告诉客户端程序连接哪台服务器主机进行RMI调用。
配置文件4个,分别是:
log4j.xml
client.properties
server.properties
rmi.policy RMISecurityManager 运用了 Java 安全策略文件来决定什么权限应赋予 RMI 应用
1. log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="default" class="org.apache.log4j.ConsoleAppender">
<param name="target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p] %d{dd MMM hh:mm:ss.SSS aa} %t [%c]%n%m%n%n" />
</layout>
</appender>
<logger name="org.quartz">
<level value="debug" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="default" />
</root>
</log4j:configuration>
2. client.properties
# Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance.
#
# Configure Main Scheduler Properties ======================================
org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.logger = schedLogger
org.quartz.scheduler.rmi.proxy = true
org.quartz.scheduler.rmi.registryHost = localhost
org.quartz.scheduler.rmi.registryPort = 1099
3. server.properties
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.rmi.export = true
org.quartz.scheduler.rmi.registryHost = localhost
org.quartz.scheduler.rmi.registryPort = 1099
org.quartz.scheduler.rmi.createRegistry = true
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
4. SimpleJob.java
package com.zbaccp.quartz.cl.example12;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* 本任务无特殊之处,特殊的在于c/s机制,运行时仅仅打印出任务名和运行时间
*
* @author 林艺
*/
public class SimpleJob implements Job {
public static final String MESSAGE = "msg";
private static Log _log = LogFactory.getLog(SimpleJob.class);
public SimpleJob() {}
/**
*
* 任务体<br/> 当与任务关联的触发器触发式时,由Scheduler调用
*
*/
public void execute(JobExecutionContext context)
throws JobExecutionException {
// 仅仅打印出任务名和运行时间
String jobName = context.getJobDetail().getFullName();
String message = (String) context.getJobDetail().getJobDataMap().get(MESSAGE);
_log.info("SimpleJob: " + jobName + " executing at " + new Date());
_log.info("SimpleJob: msg: " + message);
}
}
5. RemoteServerExample.java
package com.zbaccp.quartz.cl.example12;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.impl.StdSchedulerFactory;
/**
本范例演示quartz在client/server环境,利用rmi(远程方法调用)在远程服务器端安排job。
本例中首先运行服务器端以启动排程,但是不安排任何任务。客户端程序将使用RMI连接远程的服务器端,并且安排job。
一旦安排好job,服务器端排程将在正确的时间运行任务。(译者注:任务什么时候运行依赖于触发器)
* @author 林艺
*
*/
public class RemoteServerExample {
public void run() throws Exception {
Log log = LogFactory.getLog(RemoteServerExample.class);
System.setProperty("org.quartz.properties", "server.properties");
//1-- 排程实例化
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
log.info("------- 初始化完毕 -----------");
log.info("------- (没有预先安排任何任务,任务依赖于远程客户端程序 --");
log.info("------- 排程开始启动 ----------------");
//2-- 启动排程
sched.start();
log.info("------- 排程启动完毕 -----------------");
log.info("------- 等待10分钟... ------------");
//3-- 等待10分钟
try {
Thread.sleep(600L * 1000L);
} catch (Exception e) {
}
//4-- 关闭排程
log.info("------- 排程开始关闭 ---------------------");
sched.shutdown(true);
log.info("------- 排程已经关闭 -----------------");
SchedulerMetaData metaData = sched.getMetaData();
log.info("共执行任务 " + metaData.numJobsExecuted() + " 次。");
}
public static void main(String[] args) throws Exception {
RemoteServerExample example = new RemoteServerExample();
example.run();
}
}
6. RemoteClientExample.java
package com.zbaccp.quartz.cl.example12;
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.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
/**
* 本范例是quartz c/s程序的客户端,将远程在服务器端排程安排任务。<br/>
* 本程序的核心在于配置文件
*
* @author 林艺
*/
public class RemoteClientExample {
public void run() throws Exception {
Log log = LogFactory.getLog(RemoteClientExample.class);
System.setProperty("org.quartz.properties", "client.properties");
//1-- 排程实例化
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
//2-- 定义任务和触发器
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 * * ? * *");
//3-- 安排任务
sched.scheduleJob(job, trigger);
log.info("远程任务已经安排!");
}
public static void main(String[] args) throws Exception {
RemoteClientExample example = new RemoteClientExample();
example.run();
}
}
7. 我们的结论
1) Quartz远程功能的实现依赖于java的底层rmi,可能有些大侠做过ejb,ejb的底层同样依赖于java rmi。
2) Rmi必须首先运行rmi注册表服务,在注册表中注册远程对象,客户端必须到注册表服务中去查找,才能获取远程对象的引用。(类似:ejb到jndi树上查找远程home对象的引用)
3) 客户端一旦获取远程对象的引用,就可以与远程对象通讯(方法调用)。通讯的机制为stub存根和skeleton骨架。
4) Rmi远程过程调用,必然使用代理,代理的目的是将远程的客户端方法调用,转为本地的方法调用(skeleton本地调用real subject),所谓本地指服务器端目标程序方。
5) 本例中代理主题为stb,skeleton没有任何理由实现subject接口,它只是一个线程而已。
6) 本程序说明文字一再强调服务器程序和客户端程序最好运行在不同计算机上,是因为有rmi。
7) 远程计算机的通讯底层依赖于套接字,套接字包含主机和端口。
8) 如果我说的第2,3,4条你不能理解,最好学一下EJB或者RMI
9) 第7条不能理解,学一下套接字socket
8. properties中配置的说明
rmi注册表服务监听在1099端口
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.rmi.export = true
org.quartz.scheduler.rmi.registryHost = localhost
org.quartz.scheduler.rmi.registryPort = 1099
org.quartz.scheduler.rmi.createRegistry = true
客户端socket连接rmi注册表服务
# Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance.
#
# Configure Main Scheduler Properties ======================================
org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.logger = schedLogger
org.quartz.scheduler.rmi.proxy = true
org.quartz.scheduler.rmi.registryHost = localhost
org.quartz.scheduler.rmi.registryPort = 1099
文件下载:quartzOfficial12.rar