喜歡從業的專注,七分學習的態度。
概述
Java項目定時任務(JOB)首選quartz,一般我們選擇集成到Spring中變成Spring+Quartz。拋開API和復雜的部署邏輯,簡述簡單配置經驗。 根據實際部署情況需要區分:單獨環境,集群環境。
單獨環境:只有一臺機器運行JOB,不存在任務沖突和搶任務問題。
集群環境:存在多個機器多個應用服務器,每個應用都會運行JOB,產生任務、事物搶占等一系列資源沖突問題,在集群環境下需要保證多個應用服務器中同一時間只有一個應用服務運行JOB。
原理簡述
-
配置步驟
要配置一個能執行任務類的JOB分為三部分,配置JOB內容、配置執行JOB的觸發器、配置觸發器管理工廠。
-
配置JOB內容
配置一個bean,用來指定Job需要運行的類和方法。具體在Spring的xml中配置,區分單獨環境和集群環境。單獨環境
配置 一個job執行 deviceActivatedService 的execute 方法:
<bean id="deviceActivateGetResultJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<ref bean="deviceActivatedService"/>
</property>
<property name="targetMethod">
<value>execute</value>
</property>
<property name="concurrent" value="false"/>
</bean>集群環境
集群環境需要將JOB持久化,才能實現集群中唯一執行。就是將JOB信息寫入到數據庫表中,Quartz利用自帶創建表腳本自動創建對應需要的表,將JOB和觸發器寫入數據庫,并將每個運行中的應用服務器編個實例名同步到對應需要執行的數據中,實時讀取更新庫表信息,達到控制JOB目的。持久化的JOB需要jobDataMap數據,通常把要執行的類和方法放到Map中,新增一個Job調度類來進行調度,執行Job時取Map數據進行實力化。例如新增DetailQuartzJobBean 類進行調度,執行類和執行方法配置在Map中。
調度類:DetailQuartzJobBean,EasyApplicationContextUtils指spring的實例化類,根據自己項目情況進行引用。可修改為 ApplicationContext ctx;,使用ctx.getBean(beanName)獲取類。
import com.ztesoft.resmaster.util.EasyApplicationContextUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.lang.reflect.Method;
public class DetailQuartzJobBean extends QuartzJobBean {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
private String targetObject;
private String targetMethod;
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
try {
if(targetObject==null){
targetObject=context.getJobDetail().getJobDataMap().getString("targetObject");
targetMethod=context.getJobDetail().getJobDataMap().getString("targetMethod");
}
logger.info("execute [" + targetObject + "] at once>>>>>>");
Object otargetObject = EasyApplicationContextUtils.getBeanByName(targetObject);
Method m = null;
try {
m = otargetObject.getClass().getMethod(targetMethod, new Class[]{});
m.invoke(otargetObject, new Object[]{});
} catch (SecurityException e) {
e.printStackTrace();
logger.error(e.getMessage());
} catch (NoSuchMethodException e1) { e1.printStackTrace();
logger.error(e1.getMessage());
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
public void setTargetObject(String targetObject) {
this.targetObject = targetObject;
}
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
}
配置信息,jobClass配置調度類,需要運行的Job配置到jobDataAsMap中。需要注意的是name這個屬性在項目中的所有Job中不能重復。
<bean id="saveOrderToDatabaseJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>com.ztesoft.resmaster.module.resservice.quartz.DetailQuartzJobBean</value>
</property>
<property name="name" value="Custom-Detail-Quartz-Job-Bean_saveOrderToDatabaseJob"/>
<property name="jobDataAsMap">
<map>
<entry key="targetObject" value="qinghOrderGeneratedJob"/>
<entry key="targetMethod" value="generateOrderInfo"/>
<entry key="concurrent " value="true"/>
</map>
</property>
</bean> 配置觸發器
Job內容主要是配置Job需要執行的類,而配置觸發器是給配置的Job設置一個執行時間和執行周期,一個Job可以被多個觸發器關聯執行。關鍵屬性,jobDetail和cronExpression。關聯Job配置在jobDetail屬性中,執行周期配置在cronExpression中。
cronExpression的時間采用cron時間表達式,具體時間格式最簡單說明就是第1位是秒,第2位是分鐘,第3位是小時,第4位是日期,斜杠(代表每)。
如:
每分鐘到第10秒的時候執行,10 * * * * ?
每小時到第15分鐘的時候執行,0 15 * * * ?
每天到20點15分鐘20秒的時候執行,20 15 20 * * ?
每5秒執行,0/5 * * * * ?
每5分鐘執行,0 0/5 * * * ?
每小時執行,0 0 1 * * ?
涉及到更多復雜公式使用時,百度"cron時間表達式"
-
配置觸發器管理工廠
管理工廠是觸發器和Job的總管理處,控制quartz的基礎屬性和觸發器的啟用。在spring配置文件中配置SchedulerFactoryBean來實現。主要屬性 triggers,triggers下list配置需要啟用的觸發器。單獨環境
獨立的環境直接在triggers下配置觸發器列表。
<bean id="schedulerFactory" lazy-init="false" autowire="no"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="saveOrderToDatabaseJobTrigger"/>
</list>
</property>
</bean>
集群環境
集群環境相對于單獨環境配置信息更復雜,需要配置集群屬性、數據源、實例名信息。通常通過加載quartz.properties配置文件,將信息配置到quartz.properties文件中,當然也可以增加quartzProperties屬性,將quartz.properties中的內容配置到quartzProperties節點下面,效果是一樣的。
管理類配置
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">quartzScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">3</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.MSSQLDelegate </prop>
<prop key="org.quartz.jobStore.selectWithLockSQL">SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
</props>
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="autoStartup" value="true" />
<property name="triggers"><list><ref bean="saveOrderToDatabaseJobTrigger"/>
</list>
</property>
</bean>
注:properties屬性中可以在百度中搜"quartz.properties詳細配置",了解含義后根據項目實際情況進行調整。
其中:
- org.quartz.jobStore.isClustered代表是否集群,集群環境必須配置true,
- org.quartz.jobStore.driverDelegateClass代表數據驅動類型,Oracle可以不用配置該屬性,千萬不要配置錯誤,因為數據庫結構的差異,在配置錯誤后,會導致Map等相關Job數據取數取不到而實例化對象為空,調度不到實際Job。
集群持久化后的JOB可以在三個表中檢查JOB配置和運行情況,QRTZ_JOB_DETAILS、QRTZ_TRIGGERS、QRTZ_CRON_TRIGGERS。
<small>堅持積累,堅持學習。</small>