Quartz 是 OpenSymphony 開源組織在任務調度領域的一個開源項目,完全基於 Java 實現。作爲一個優秀的開源調度框架,Quartz 具有功能強大,應用靈活,易於集成的特點
目前項目處於運維階段中期,事情不多,所以有時間將自己用過的技術以及想要學習的技術整理一下,所以萌生一個自己做項目的想法,通過自己做項目將這些年使用的技術以及想要學習和深入瞭解的技術做一個總結,做項目第一件事就是給項目起名字,發現起名字這件事說簡單也簡單,說難也難,想取一個響亮點名字吧才發現自己語言有多麼的匱乏,不過還好,由於最近再玩魔方,所以就用魔方的單詞來作爲項目的名字,反正最終目的是爲了對自己這幾年技術的總結,無所謂了.....
上圖就是自己想到的項目模塊,當然後續還會在增加,想到一些就加一些,目前只是實現了系統管理也就是權限部分還有系統監控部分的作業監控
項目簡介:
項目名稱:CUBE
項目使用的開發語言:Java
項目框架:Struts2+Spring+Hibernate+Maven
服務器:Tomcat
數據庫:Mysql
開發工具:Eclipse
前端:easyui
目前CUBE項目的效果如圖:
登陸界面:
登陸後的主界面
美工這活兒真心不好做啊,現在主界面的效果就是這樣了,top和bottom還需要在美化一下,後期在做吧
下面是作業監控的最終效果:
觸發器添加界面,觸發器的時間規則使用CronTrigger
上邊就是CUBE系統中的作業監控部分的最終實現
Quartz是Java領域最著名的開源任務調度工具。Quartz提供了極爲廣泛的特性如持久化任務,集羣和分佈式任務等,其特點如下:
- 完全由Java寫成,方便集成(Spring)
- 伸縮性
- 負載均衡
- 高可用性
Quartz數據庫核心表如下:
Table Name | Description |
---|---|
QRTZ_CALENDARS | 存儲Quartz的Calendar信息 |
QRTZ_CRON_TRIGGERS | 存儲CronTrigger,包括Cron表達式和時區信息 |
QRTZ_FIRED_TRIGGERS | 存儲與已觸發的Trigger相關的狀態信息,以及相聯Job的執行信息 |
QRTZ_PAUSED_TRIGGER_GRPS | 存儲已暫停的Trigger組的信息 |
QRTZ_SCHEDULER_STATE | 存儲少量的有關Scheduler的狀態信息,和別的Scheduler實例 |
QRTZ_LOCKS | 存儲程序的悲觀鎖的信息 |
QRTZ_JOB_DETAILS | 存儲每一個已配置的Job的詳細信息 |
QRTZ_SIMPLE_TRIGGERS | 存儲簡單的Trigger,包括重複次數、間隔、以及已觸的次數 |
QRTZ_BLOG_TRIGGERS | Trigger作爲Blob類型存儲 |
QRTZ_TRIGGERS | 存儲已配置的Trigger的信息 |
目前CUBE系統作業監控只是用到了上述加粗部分的表,其餘表沒有用到
Quartz的執行邏輯如下:
Quartz 任務調度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任務調度的元數據, scheduler 是實際執行調度的控制器。
Quartz 中,trigger 是用於定義調度時間的元素,即按照什麼時間規則去執行任務。Quartz 中主要提供了四種類型的 trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger。這四種 trigger 可以滿足企業應用中的絕大部分需求。我們將在企業應用一節中進一步討論四種 trigger 的功能。
Quartz 中,job 用於表示被調度的任務。主要有兩種類型的 job:無狀態的(stateless)和有狀態的
(stateful)。對於同一個 trigger 來說,有狀態的 job 不能被並行執行,只有上一次觸發的任務被執行完之後,才能觸發下一次執行。Job 主要有兩種屬性:volatility 和 durability,其中 volatility 表示任務是否被持久化到數據庫存儲,而 durability 表示在沒有 trigger 關聯的時候任務是否被保留。兩者都是在值爲 true 的時候任務被持久化或保留。一個 job 可以被多個 trigger 關聯,但是一個 trigger 只能關聯一個 job。
Quartz 中, scheduler 由 scheduler 工廠創建:DirectSchedulerFactory 或者 StdSchedulerFactory。 第二種工廠 StdSchedulerFactory 使用較多,因爲 DirectSchedulerFactory 使用起來不夠方便,需要作許多詳細的手工編碼設置。 Scheduler 主要有三種:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。CUBE以最常用的 StdScheduler 實現。
說了很多Quartz的理論,就不在多說了,自己目前也就理解到這裏了,下面是Quartz監控的代碼
applicationContext-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config /> <context:component-scan base-package="com.cube.*" /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="cube"></property> <!-- value 對應persistence.xml中的 persistence-unit name --> </bean> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- login action --> <tx:annotation-driven transaction-manager="txManager" /> </beans>
applicationContext-quartz.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="applicationContextSchedulerContextKey" value="applicationContextKey" /> <property name="configLocation" value="classpath:quartz.properties" /> </bean> <bean name="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass"> <value>com.cube.service.quartz.QuartzJobService</value> </property> <property name="durability" value="true" /> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/cube?useUnicode=true&characterEncoding=UTF8" /> <property name="user" value="root" /> <property name="password" value="" /> <property name="initialPoolSize" value="10" /> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="25" /> <property name="acquireIncrement" value="5" /> <property name="maxIdleTime" value="7200" /> </bean> </beans>
quartz.properties
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = false
org.quartz.jobStore.maxMisfiresToHandleAtATime=1
Schedule調度器Service
public interface SchedulerService {
public List<Map<String, Object>> getQrtzTriggers();
/**
* 根據 Quartz Cron Expression 調試任務
*
* @param name Quartz CronTrigger名稱
* @param group Quartz CronTrigger組
* @param cronExpression Quartz Cron 表達式,如 "0/10 * * ? * * *"等
*/
void schedule(String name, String group, String cronExpression);
/**
* 根據 Quartz Cron Expression 調試任務
*
* @param name Quartz CronTrigger名稱
* @param group Quartz CronTrigger組
* @param cronExpression Quartz CronExpression
*/
void schedule(String name, String group, CronExpression cronExpression);
/**
*
* @param name Quartz CronTrigger名稱
* @param group Quartz CronTrigger組
* @param cronExpression Quartz CronExpression
*/
void schedule(String name, String group);
void schedule(Map<String, Object> map);
/**
* 暫停觸發器
*
* @param triggerName 觸發器名稱
*/
void pauseTrigger(String triggerName);
/**
* 暫停觸發器
*
* @param triggerName 觸發器名稱
* @param group 觸發器組
*/
void pauseTrigger(String triggerName, String group);
/**
* 恢復觸發器
*
* @param triggerName 觸發器名稱
*/
void resumeTrigger(String triggerName);
/**
* 恢復觸發器
*
* @param triggerName 觸發器名稱
* @param group 觸發器組
*/
void resumeTrigger(String triggerName, String group);
/**
* 刪除觸發器
*
* @param triggerName 觸發器名稱
* @return
*/
boolean removeTrigdger(String triggerName);
/**
* 刪除觸發器
*
* @param triggerName 觸發器名稱
* @param group 觸發器組
* @return
*/
boolean removeTrigdger(String triggerName, String group);
}
@Transactional
@Service("schedulerService")
public class SchedulerServiceImpl implements SchedulerService {
@Resource(name = "quartzDao")
private QuartzDao quartzDao;
@Autowired
private Scheduler scheduler;
@Autowired
private JobDetail jobDetail;
private static final String NULLSTRING = null;
private static final Date NULLDATE = null;
/**
*
* @param name
* @param group
* @param cronExpression
* @see com.cube.service.SchedulerService#schedule(java.lang.String, java.lang.String,
* java.lang.String)
*/
public void schedule(String name, String group, String cronExpression) {
try {
schedule(name, group, new CronExpression(cronExpression));
} catch (ParseException e) {
e.printStackTrace();
}
}
/**
* @param name
* @param group
* @param cronExpression
* @see com.cube.service.SchedulerService#schedule(java.lang.String, java.lang.String,
* org.quartz.CronExpression)
*/
public void schedule(String name, String group, CronExpression cronExpression) {
if (name == null || name.trim().equals("")) {
name = UUID.randomUUID().toString();
}
CronTriggerImpl trigger = new CronTriggerImpl();
trigger.setCronExpression(cronExpression);
TriggerKey triggerKey = new TriggerKey(name, group);
trigger.setJobName(jobDetail.getKey().getName());
trigger.setKey(triggerKey);
try {
scheduler.addJob(jobDetail, true);
if (scheduler.checkExists(triggerKey)) {
scheduler.rescheduleJob(triggerKey, trigger);
} else {
scheduler.scheduleJob(trigger);
}
} catch (SchedulerException e) {
throw new IllegalArgumentException(e);
}
}
/**
* @param map
* @see com.cube.service.SchedulerService#schedule(java.util.Map)
*/
public void schedule(Map<String, Object> map) {
// TODO Auto-generated method stub
}
/**
* @param triggerName
* @see com.cube.service.SchedulerService#pauseTrigger(java.lang.String)
*/
public void pauseTrigger(String triggerName) {
// TODO Auto-generated method stub
}
/**
* @param triggerName
* @param group
* @see com.cube.service.SchedulerService#pauseTrigger(java.lang.String, java.lang.String)
*/
public void pauseTrigger(String triggerName, String group) {
try {
scheduler.pauseTrigger(new TriggerKey(triggerName, group));
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param triggerName
* @see com.cube.service.SchedulerService#resumeTrigger(java.lang.String)
*/
public void resumeTrigger(String triggerName) {
// TODO Auto-generated method stub
}
/**
* @param triggerName
* @param group
* @see com.cube.service.SchedulerService#resumeTrigger(java.lang.String, java.lang.String)
*/
public void resumeTrigger(String triggerName, String group) {
TriggerKey triggerKey = new TriggerKey(triggerName, group);
try {
scheduler.resumeTrigger(triggerKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param triggerName
* @return
* @see com.cube.service.SchedulerService#removeTrigdger(java.lang.String)
*/
public boolean removeTrigdger(String triggerName) {
return removeTrigdger(triggerName, NULLSTRING);
}
/**
* @param triggerName
* @param group
* @return
* @see com.cube.service.SchedulerService#removeTrigdger(java.lang.String, java.lang.String)
*/
public boolean removeTrigdger(String triggerName, String group) {
TriggerKey triggerKey = new TriggerKey(triggerName, group);
try {
scheduler.pauseTrigger(triggerKey);// 停止觸發器
return scheduler.unscheduleJob(triggerKey);// 移除觸發器
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
/**
* @return
* @see com.cube.service.SchedulerService#getQrtzTriggers()
*/
public List<Map<String, Object>> getQrtzTriggers() {
return quartzDao.getQrtzTriggers();
}
/**
* @param name
* @param group
* @see com.cube.service.SchedulerService#schedule(java.lang.String, java.lang.String)
*/
public void schedule(String name, String group) {
schedule(name, group, NULLSTRING);
}
}
QuartzDao,主要負責查詢觸發器的狀態信息
@Repository("quartzDao")
public class QuartzDao {
private DataSource dataSource;
@Autowired
public void setDataSource(@Qualifier("dataSource") DataSource dataSource) {
this.dataSource = dataSource;
}
// 查詢Trigger
public List<Map<String, Object>> getQrtzTriggers() {
List<Map<String, Object>> results = getJdbcTemplate().queryForList(
"select * from QRTZ_TRIGGERS order by start_time");
long val = 0;
String temp = null;
for (Map<String, Object> map : results) {
temp = MapUtils.getString(map, "trigger_name");
if (StringUtils.indexOf(temp, "&") != -1) {
map.put("display_name", StringUtils.substringBefore(temp, "&"));
} else {
map.put("display_name", temp);
}
val = MapUtils.getLongValue(map, "next_fire_time");
if (val > 0) {
map.put("next_fire_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}
val = MapUtils.getLongValue(map, "prev_fire_time");
if (val > 0) {
map.put("prev_fire_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}
val = MapUtils.getLongValue(map, "start_time");
if (val > 0) {
map.put("start_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}
val = MapUtils.getLongValue(map, "end_time");
if (val > 0) {
map.put("end_time", DateFormatUtils.format(val, "yyyy-MM-dd HH:mm:ss"));
}
map.put("trigger_state", Constant.status.get(MapUtils.getString(map, "trigger_state")));
}
return results;
}
private JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(this.dataSource);
}
}
JobClass
public class QuartzJobService extends QuartzJobBean {
// 負責所有任務的調度
private TaskService taskService;
/**
* @param context
* @throws JobExecutionException
* @see org.springframework.scheduling.quartz.QuartzJobBean#executeInternal(org.quartz.JobExecutionContext)
*/
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
String triggerName = context.getTrigger().getKey().getName();
taskService = (TaskService) getApplicationContext(context).getBean("taskService");
taskService.execute(triggerName);
}
private ApplicationContext getApplicationContext(final JobExecutionContext jobexecutioncontext) {
try {
return (ApplicationContext) jobexecutioncontext.getScheduler().getContext()
.get("applicationContextKey");
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
}
TaskService,負責所有任務的調用
public interface TaskService {
/**
* @param triggerName
*/
public void execute(String triggerName);
}
@Transactional
@Service("taskService")
public class TaskServiceImpl implements TaskService {
//此處注入自己業務的Service
@Resource(name = "reportService")
private ReportService reportService;
/**
* 根據TriggerName 調用不同的業務邏輯service
*
* @param triggerName
* @see com.cube.service.TaskService#execute(java.lang.String)
*/
public void execute(String triggerName) {
//此處根據觸發器名稱調用相應的業務邏輯Service
if ("reportTigger".equalsIgnoreCase(triggerName)) {
reportService.createReport();
} else {
System.out.println(triggerName + ":企業業務邏輯");
}
}
}
下面是Action類的實現,負責添加、刪除、暫停、恢復Trigger
@Controller
@Scope("prototype")
public class TriggerAction extends BaseAction<TriggerEntity> {
/** */
private static final long serialVersionUID = -3326354633384499660L;
private TriggerEntity triggerEntity = getModel();
private Map jsonMap = new HashMap();
@Resource(name = "schedulerService")
private SchedulerService schedulerService;
/**
* 跳轉到tigger 管理界面
*
* @return
*/
public String trigger() {
return "trigger";
}
/**
* 分頁查詢Trigger
*
* @return
*/
public String list() {
List<Map<String, Object>> list = schedulerService.getQrtzTriggers();
jsonMap.put("rows", list);
jsonMap.put("total", list.size());
return "list";
}
/**
* 添加觸發器
*
* @return
*/
public String save() {
// 獲取界面以參數
String triggerName = triggerEntity.getTrigger_name();
String cronExpression = triggerEntity.getCron();
String group = triggerEntity.getTrigger_group();
schedulerService.schedule(triggerName, group, cronExpression);
jsonMap.put("flag", true);
return "save";
}
/**
* 暫停
*
* @return
*/
public String pause() {
schedulerService.pauseTrigger(triggerEntity.getTrigger_name(),
triggerEntity.getTrigger_group());
jsonMap.put("flag", true);
return "pause";
}
/**
* Trigger恢復
*
* @return
*/
public String play() {
schedulerService.resumeTrigger(triggerEntity.getTrigger_name(),
triggerEntity.getTrigger_group());
jsonMap.put("flag", true);
return "play";
}
/**
* 刪除
*
* @return
*/
public String deleteTrigger() {
schedulerService.removeTrigdger(triggerEntity.getTrigger_name(),
triggerEntity.getTrigger_group());
jsonMap.put("flag", true);
return "deleteTrigger";
}
public Map getJsonMap() {
return jsonMap;
}
public void setJsonMap(Map jsonMap) {
this.jsonMap = jsonMap;
}
}
到此,作業監控功能就實現了,當然實現方式不止這一種,希望有別的實現大家可以共享一下吧,互相學習!!
下載地址:http://download.csdn.net/detail/zhouhua0104/9611192