SpringMvc + Quarzt 動態執行任務實現過程

Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它可以與J2EE與J2SE應用程序相結合也可以單獨使用。Quartz可以用來創建簡單或爲運行十個,百個,甚至是好幾萬個Jobs這樣複雜的程序。Jobs可以做成標準的Java組件或 EJBs。Quartz的最新版本爲Quartz 2.2.1。

本次遇到的情況是 關於N多個抓取任務的調度,並且由於各任務本身是有關聯性,不易改造成標準的Jobs,並考慮到不去入侵和修改原來的任務方法,故期望運用Quartz按照Cron設定的時間去執行指定類和方法。

因爲需要保存數據到數據庫,設計了一個JavaBean來存儲數據結構如下:

public class Quartz {
    /** 自動編號 */
    private Long              SCHEDULE_ID;
    /** 任務名稱 */
    private String            NAME;
    /** 任務別名 */
    private String            ALIASNAME;
    /** 任務分組 */
    private String            GROUP;
    /** 任務執行類-全路徑 */
    private String            CLASS;
    /** 任務執行類-方法 */
    private String            METHOD;
    /** 任務Cron表達式 */
    private String            CRON;
    /** 任務描述 */
    private String            DESC;
    private int               STATUS_ID;
    private Long              CREATETIME;
    private Long              UPDATETIME;
    private int               CREATEUSER;
    private int               UPDATEUSER;
}

Quartz 將作業劃分爲:執行器(JobDetail)、觸發器(Trigger)、監聽器(Listener),簡略說就是執行器決定如何執行任務,觸發器決定何時執行任務,監聽器決定任務到達某狀態如何操作。

Quartz典型的JobDetail是實現一個Job接口,然後新建一個觸發器去執行該Job。

典型的Quartz應用一:

import java.util.Date;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.quartz.Job;  
import org.quartz.JobExecutionContext;  
import org.quartz.JobExecutionException;  

public class HelloJob implements Job {  

    private static Logger _log = LoggerFactory.getLogger(HelloJob.class);  

    public HelloJob() {  

    }  

    public void execute(JobExecutionContext context)  
        throws JobExecutionException {  

        _log.error(" 執行任務: " + new Date());  

    }  
}  

測試類

import static org.quartz.JobBuilder.newJob;  
import static org.quartz.TriggerBuilder.newTrigger;  
import static org.quartz.DateBuilder.*;  
import java.util.Date;  
import org.quartz.JobDetail;  
import org.quartz.Scheduler;  
import org.quartz.SchedulerFactory;  
import org.quartz.Trigger;  
import org.quartz.impl.StdSchedulerFactory;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
public class SimpleExample {  

    private static Logger log = LoggerFactory.getLogger(SimpleExample.class);  

    public void run() throws Exception {  

        // 通過SchedulerFactory獲取一個調度器實例  
        SchedulerFactory sf = new StdSchedulerFactory();      

        Scheduler sched = sf.getScheduler();  

        Date runTime = evenMinuteDate(new Date());  

        // 通過過JobDetail封裝HelloJob,同時指定Job在Scheduler中所屬組及名稱,這裏,組名爲group1,而名稱爲job1。  
        JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();  

        // 創建一個SimpleTrigger實例,指定該Trigger在Scheduler中所屬組及名稱。  
        // 接着設置調度的時間規則.當前時間運行  
        Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();  

        // 註冊並進行調度  
        sched.scheduleJob(job, trigger);  

        // 啓動調度器  
        sched.start();  

        try {  
            //當前線程等待65秒  
            Thread.sleep(65L * 1000L);  
        } catch (Exception e) {  

        }  
        //調度器停止運行  
        sched.shutdown(true); 
        log.error("結束運行。。。。");      
    }  

    public static void main(String[] args) throws Exception {  
        SimpleExample example = new SimpleExample();  
        example.run();  
    }  
}  

典型的Quartz應用二:

<!-- 使用MethodInvokingJobDetailFactoryBean,任務類可以不實現Job接口,通過targetMethod指定調用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="group" value="job_work"/>
    <property name="name" value="job_work_name"/>
    <!--false表示等上一個任務執行完後再開啓新的任務-->
    <property name="concurrent" value="false"/>
    <property name="targetObject">
        <ref bean="taskJob"/>
    </property>
    <property name="targetMethod">
        <value>execute</value>
    </property>
</bean>
<!--  調度觸發器 -->
<bean id="myTrigger"
      class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="name" value="work_default_name"/>
    <property name="group" value="work_default"/>
    <property name="jobDetail">
        <ref bean="jobDetail" />
    </property>
    <property name="cronExpression">
        <value>0/5 * * * * ?</value>
    </property>
</bean>
<!-- 調度工廠 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="myTrigger"/>
        </list>
    </property>
</bean>

應用一是常見的java實現方式,沒有使用Spring去管理Quartz對象,也反映Quartz可以脫離Spring獨立執行。應用二是通過Spring配置文件實現執行實例化類的方法,但是把配置寫在XML文件裏面非常不方便修改。因此借鑑第二種方法實現Quartz的動態加載任務,同理也可實現增加、刪除、暫停、啓動、單次執行等功能。

考慮到Quartz調度工廠不需要多例,並且考慮注入的便捷性,在SpringMvc.xml文件裏配置:

<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"/>

並將其注入到SchedulerJobService中。

package cn.focus.sh.core.quartz.service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.stereotype.Service;

@Service
public class ScheduleJobService {

    @Autowired
    private Scheduler scheduler;

    private static Map<String,Quartz> QuartzMap = new ConcurrentHashMap<String,Quartz>();

    @Autowired
    SpringUtils utils;

    @Autowired
    private QuartzDao quartzDao;

    @PostConstruct
    public void init(){
        //從數據庫初始化任務列表
        List<Quartz> list =  quartzDao.selectAll();

        for(Quartz quartz :list){
            run(quartz);
        }
    }
    public void run(Quartz quartz){
        MethodInvokingJobDetailFactoryBean bean =new MethodInvokingJobDetailFactoryBean();
        try {
            Class<?> loader = Class.forName(quartz.getCLASS());
            bean.setTargetObject(utils.getBean(loader));
            bean.setTargetClass(loader);
            bean.setTargetMethod(quartz.getMETHOD());
            bean.setGroup(quartz.getGROUP());
            bean.setName(quartz.getNAME());
            bean.setConcurrent(false);  
            bean.afterPropertiesSet();

            //表達式調度構建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCRON());

            //按新的cronExpression表達式構建一個新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartz.getNAME(), quartz.getGROUP())
                .withSchedule(scheduleBuilder).build();

            try {
                scheduler.scheduleJob(bean.getObject(), trigger);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }
}

CLASS cn.xxx.xxx.manager.xxxManager
METHOD getCount
CRON 0/10 * * * * ?

目前只實現了動態執行任務,同理可以實現動態暫停、刪除、增加、修改等功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章