1. 程式人生 > >quartz 可配置的定時服務

quartz 可配置的定時服務

一、可配置的定時服務先設計資料庫表結構

CREATE TABLE `job` (
	`id` CHAR(36) NOT NULL,
	`name` VARCHAR(36) NOT NULL COMMENT '任務名',
	`bean_name` VARCHAR(100) NOT NULL COMMENT 'spring bean name',
	`execute_exp` VARCHAR(200) NOT NULL COMMENT '執行的表示式',
	`state` INT(11) NOT NULL DEFAULT '1' COMMENT '1有效2無效',
	`remark` VARCHAR(100) NULL DEFAULT '1' COMMENT '描述資訊',
	`create_time` DATETIME NOT NULL,
	PRIMARY KEY (`id`)
)
二、定義QuartzManager 管理器 可以使得注入可用
public class QuartzManager {
    private static SchedulerFactory sf                         = null;
    private static String           Default_JOB_GROUP_NAME     = "job_group";
    private static String           Default_TRIGGER_GROUP_NAME = "job_trigger";
    
    static {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = QuartzManager.class.getClassLoader();
        }
        
        // 載入屬性檔案app.properties
        InputStream is = null;
        try {
            is = classLoader.getResourceAsStream("conf/quartz.properties");
            Properties properties = new Properties();
            properties.load(is);
            is.close();
            is = null;
            sf = new StdSchedulerFactory(properties);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    
    /**
     * 新增任務
     * 
     * @param jobName
     * @param triggerName
     * @param jobClass
     * @param time
     * @return
     * @throws SchedulerException
     * @throws ParseException
     */
    public static JobDetail addJob(String jobId, String triggerName, String beanName, Class<? extends Job> jobClass,
            String time) throws SchedulerException, ParseException {
        Scheduler sched = sf.getScheduler();
        JobDetail jobDetail = newJob(jobClass).withIdentity(jobId, Default_JOB_GROUP_NAME).build();// 任務名,任務組,任務執行類
        jobDetail.getJobDataMap().put("beanName", beanName);
        jobDetail.getJobDataMap().put("jobId", jobId);
        CronTrigger trigger = newTrigger().withIdentity(triggerName, Default_TRIGGER_GROUP_NAME)
                .withSchedule(cronSchedule(time)).build();
        sched.scheduleJob(jobDetail, trigger);
        if (!sched.isShutdown()) sched.start();
        return jobDetail;
    }
    
    public static List<? extends Trigger> getTriggerKeys(JobKey jobKey) throws SchedulerException {
        return sf.getScheduler().getTriggersOfJob(jobKey);
    }
    
    /**
     * 觸發一個任務
     * 
     * @param jobKey
     * @throws SchedulerException
     */
    public static void triggerJob(JobKey jobKey) throws SchedulerException {
        sf.getScheduler().triggerJob(jobKey);
    }
    
    /**
     * 得到一個觸發器的任務狀態
     * 
     * @param triggerKey
     * @return
     * @throws SchedulerException
     */
    public static TriggerState getTriggerState(TriggerKey triggerKey) throws SchedulerException {
        return sf.getScheduler().getTriggerState(triggerKey);
    }
    
    /**
     * 移除任務
     * 
     * @param jobName
     * @param triggerName
     * @param jobClass
     * @param time
     * @return
     * @throws SchedulerException
     * @throws ParseException
     */
    public static void removeJob(JobKey jobKey) throws SchedulerException {
        Scheduler sched = sf.getScheduler();
        sched.pauseJob(jobKey);
        sched.deleteJob(jobKey);
    }
    
    public static void clearAll() throws SchedulerException {
        Scheduler sched = sf.getScheduler();
        sched.clear();
        sched.shutdown();
    }
}
三、定義介面類
public interface IQuartzBaseJobService {
    public boolean execute(Job job);
}
四、每個新的beanName 實現 IQuartzBaseJobService走自己的邏輯

五、考慮到每個介面只實現,自己的業務不能加日誌等資訊,需要定義一個類來完成這事

public class QuartzSpringBeanJob implements org.quartz.Job {
    private final Logger logger = LoggerFactory.getLogger(QuartzSpringBeanJob.class);
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail job = context.getJobDetail();
        String jobId = job.getJobDataMap().getString("jobId");
        String jobBeanName = job.getJobDataMap().getString("beanName");
        if (StringUtils.isEmpty(jobId) || StringUtils.isEmpty(jobBeanName)) {
            return;
        }
        boolean success = true;
        String message = "";
        // 檢查當前job是否可以執行
        IJobService jobService = SpringContextHolder.getBean("jobService");
        Job jobBean = jobService.findUnique(jobId);
        if (jobBean == null || jobBean.getState() != BooleanEnum.True.getValue()) {
            try {
                QuartzManager.removeJob(job.getKey());
            } catch (SchedulerException e) {
                logger.error(e.getMessage(), e);
                e.printStackTrace();
            }
            return;
        }
        IJobLogService jobLogService = SpringContextHolder.getBean("jobLogService");
        // 查詢上一次是否已經執行成功
        boolean existsDoing = jobLogService.existsUnComplete(jobId);
        
        // 生成job日誌
        JobLog jobLog = new JobLog();
        jobLog.setId(GuidKeyGenerator.getUUIDKey());
        jobLog.setJobId(jobId);
        jobLog.setStartTime(new Date());
        if(!existsDoing){
            jobLog.setState(JobLogStateEnum.Doing.getValue());
        } else {
            jobLog.setState(JobLogStateEnum.UnStart.getValue());
            jobLog.setMessage("上次未執行結束!本次不再執行!");
        }
        jobLogService.create(jobLog);
        if(existsDoing){// 還有沒有執行完的,就不再執行了
            return ;
        }
        
        try {
            IQuartzBaseJobService quartzBaseJobService = SpringContextHolder.getBean(jobBeanName);//為自己bean
            if (quartzBaseJobService != null) {
                quartzBaseJobService.execute(jobBean);
            }
        } catch (Exception e) {
            success = false;
            message = e.getMessage();
            logger.error(e.getMessage(), e);
        }
        // 更新日誌狀態
        JobLog newJobLog = new JobLog();
        newJobLog.setId(jobLog.getId());
        newJobLog.setState(success ? JobLogStateEnum.Success.getValue() : JobLogStateEnum.Error.getValue());
        newJobLog.setEndTime(new Date());
        newJobLog.setMessage(message);
        jobLogService.update(newJobLog, jobLog);
    }
    
六、一般spring 啟動就想執行監聽
@Service
public class JobTaskService implements IJobTaskService, Initalizer {
    private final Logger                        logger       = LoggerFactory.getLogger(JobTaskService.class);
    
    private final static Map<String, JobDetail> jobDetailMap = new HashMap<String, JobDetail>();
    
    @Autowired
    private IJobService                         jobService;
    @Autowired
    private IJobLogService                      jobLogService;
    
    @Override
    public void initalize() {
        jobLogService.clearDoing();
        startAll();
    }
    
    private void startAll() {
        List<Job> jobs = jobService.findValid();
        if (jobs == null) return;
        for (Job job : jobs) {
            try {
                JobDetail jobDetail = QuartzManager.addJob(job.getId(), "trigger-" + job.getId(), job.getBeanName(),
                        QuartzSpringBeanJob.class, job.getExecuteExp());
                jobDetailMap.put(job.getId(), jobDetail);
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("addJobError:jobId:" + job.getId() + e.getMessage(), e);
            }
        }
    }
}
七、考慮到新增修改刪除,所以我把我用的方法列
public static void removeJob(String jobName) throws SchedulerException{  
        try {  
            Scheduler sched = sf.getScheduler();
            sched.pauseTrigger(new TriggerKey(jobName, Default_TRIGGER_GROUP_NAME));// 停止觸發器  
            sched.unscheduleJob(new TriggerKey(jobName, Default_TRIGGER_GROUP_NAME));// 移除觸發器  
            sched.deleteJob(new JobKey(jobName, Default_JOB_GROUP_NAME));// 刪除任務  
        } catch (Exception e) {  
            throw new RuntimeException(e);  
        }  
    }  
    
    
    public static void modifyJobTime(String triggerName,  
            String triggerGroupName, String time) {  
        try {  
            Scheduler sched = sf.getScheduler();
            TriggerKey triggerKey = new TriggerKey(triggerName,Default_TRIGGER_GROUP_NAME);
			CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);  
			CronScheduleBuilder scheduleBuilder = cronSchedule(time);
            if (trigger == null) {  
                return;  
            }  
            String oldTime = trigger.getCronExpression();  
            if (!oldTime.equalsIgnoreCase(time)) {
            	trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
            			.withSchedule(scheduleBuilder).build();

            	//按新的trigger重新設定job執行
            	sched.rescheduleJob(triggerKey, trigger);
            }  
        } catch (Exception e) {  
            throw new RuntimeException(e);  
        }  
    }  
    
    public static void clearAll() throws SchedulerException {
        Scheduler sched = sf.getScheduler();
        sched.clear();
        sched.shutdown();
    }   
<pre name="code" class="java">    /**
     * 觸發一個任務
     * 
     * @param jobKey
     * @throws SchedulerException
     */
    public static void triggerJob(JobKey jobKey) throws SchedulerException {
        sf.getScheduler().triggerJob(jobKey);
    }


八、總結一下

Trigger 就是個觸發器 jobDetail 就是那個類要作這件事情

scheduler 一個工廠物件返回,作什麼事幾點作就可以了。

scheduler.start();

成了