quartz集羣 定時任務 改爲可配置

前面的博文中提到的quartz集羣方式會有如下缺點:java

1.假設配置了3個定時任務,job1,job2,job3,這時數據庫裏會有3條job相關的記錄,若是下次上線要停掉一個定時任務job1,那即便定時任務配置文件 quartz.xml 中的trigger 去掉了,數據庫仍是會有該條記錄,若代碼沒有去掉,那定時任務仍是會執行。spring

------解決方法1:修改該trigger的觸發時間,好比年改爲2099,讓它不觸發,(SchedulerFactoryBean中配置了  <property name="overwriteExistingJobs" value="true" />),會覆蓋數據庫中的相關記錄。數據庫

缺點:曲線救國策略,不夠好。app

 

------解決方法2:刪除數據庫中該job的相關記錄。jvm

缺點:要操做數據庫,並且表中數據還有外鍵約束,操做起來麻煩,還可能不當心破壞了其餘job信息。ui

 

解決方法3:this

介紹一種用配置文件的方式,經過開關控制,達到更細粒度的控制。主要是經過Scheduler的相關方法:Trigger和JobDetail能夠註冊到Scheduler中,二者在Scheduler中擁有各自的組及名稱,組及名稱是Scheduler查找定位容器中某一對象的依據。Scheduler定義了多個接口方法,容許外部經過組及名稱訪問和控制容器中Trigger和JobDetail。Scheduler包含了不少方法,添加、執行一次、恢復所有、刪掉任務暫停所有等方法,方便用戶靈活使用。spa

 

1.建立定時任務的配置文件 quartz-config.propertiescode

#定時任務配置文件
#key:
# CronTriggerFactoryBean配置的id
# switch:定時任務開關,ON:開啓該定時任務; OFF:關閉該定時任務
# cron:該定時任務的執行時間間隔(cron表達式)xml

#定時任務(每隔15分鐘執行)
intrBillTrigger.switch=OFF
intrBillTrigger.cron=0 0/15 * * * ?

 

2.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<bean id="intrBillJob"
          class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
          <property name="jobClass" value="com.cmcc.open.task.utils.MyDetailQuartzJobBean"></property> //參考之前的那篇博文
            <property name="Durability" value="true"/>  
                <property name="jobDataAsMap">  
            <map>  
                <entry key="targetObject" value="billTimerIntrTask" />  
                <entry key="targetMethod" value="intrBillTimer" />  
            </map> 
         </property>
    </bean>
    <bean id="intrBillTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="intrBillJob"></property>
        <!--<property name="cronExpression" value="0 0/15 * * * ?"></property>-->
        <property name="cronExpression" value="${intrBillTrigger.cron}"></property>
    </bean>

<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> 
    <property name="dataSource" ref="dataSourceQuartz"></property>
    <property name="jobFactory" ref="customJobFactory"></property>   
    <property name="applicationContextSchedulerContextKey"  value="applicationContext"></property>
    <property name="configLocation" value="classpath:quartz.properties" />  //參考之前那篇博文
    <property name="overwriteExistingJobs" value="true" />
    <property name="triggers"> 
            <list>                       
                <ref bean="intrBillTrigger"/>
            </list> 
        </property> 
    </bean> 
</beans>

3.QuartzScheduleConfigTask 定時任務控制類

package com.cmcc.open.task.service;

import com.cmcc.open.framework.utils.EncryptPropertyPlaceholderConfigurer;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * QuartzScheduleConfigTask.
 * 初始化定時任務配置
 */
@Component
public class QuartzScheduleConfigTask {
    /** The Constant log. */
    private static Logger logger = LoggerFactory.getLogger(QuartzScheduleConfigTask.class);
    private static Properties properties;

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    /**
     * 初始化定時任務配置
     */
    public void initQuartzConfig() {
        String triggerName = null;
            //讀取定時任務配置文件(包括開關、執行時間)
        Properties properties = getProperty();
        if (properties == null) {
            logger.warn(".......cannot find quartz-config properties file");
        }

        try {
            //解析定時任務配置文件,進行修改
            Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
            Scheduler sched = schedulerFactoryBean.getScheduler();
            //先對讀取到的配置進行過濾,將開關和cron放到同一個key中
            Iterator it1 = entrySet.iterator();
            while (it1.hasNext()) {
                Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>)
                    it1.next();
                String key = String.valueOf(entry.getKey());
                if (!key.endsWith("switch")) {
                    continue;
                }
                triggerName = key.substring(0, key.lastIndexOf("."));
                if ("OFF".equalsIgnoreCase(String.valueOf(entry.getValue()))) {
                    //關閉該定時任務
                    closeQuartz(sched, triggerName);
                } else {
                    //獲取該定時任務的執行時間,與配置文件進行比較
                    //目前定時任務的實現時間從配置文件中讀取,不須要再進行修改
                    String cron = null;
                    Iterator it2 = entrySet.iterator();
                    while (it2.hasNext()) {
                        Map.Entry<Object, Object> entryChild = (Map.Entry<Object, Object>) it2.next();
                        if(!String.valueOf(entryChild.getKey()).startsWith(triggerName)
                            || !String.valueOf(entryChild.getKey()).endsWith("cron")) {
                            continue;
                        }
                        cron = String.valueOf(entryChild.getValue());
                        it1.remove();
                        break;
                    }
                    updateQuartz(sched, triggerName, cron);
                }
            }
        } catch (Exception e) {
            logger.warn("Exception in trigger:" + triggerName);
            logger.warn("Exception in initQuartzConfig:" + e.getMessage(), e);
        }

    }

    /**
     * 刪除定時任務
     * @param sched
     * @param triggerName
     * @throws Exception
     */
    private void closeQuartz(Scheduler sched, String triggerName) throws Exception {
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, Scheduler.DEFAULT_GROUP);
        CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
        if (trigger == null) {
            logger.warn("no trigger get for key:" + triggerName + " do nothing for this trigger");
            return;
        }

        //若是定時任務關閉,則從數據庫中移除
        //中止觸發器
        sched.pauseTrigger(triggerKey);
        //移除觸發器
        sched.unscheduleJob(triggerKey);
        //刪除任務
        String jobName = trigger.getJobKey().getName();
        sched.deleteJob(JobKey.jobKey(jobName, Scheduler.DEFAULT_GROUP));
        logger.info("delete quartz job:" + jobName + " succ.........");
    }

    /**
     * 更新定時任務執行時間
     * @param sched
     * @param triggerName
     * @param cron
     * @throws Exception
     */
    private void updateQuartz(Scheduler sched, String triggerName, String cron) throws Exception {
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, Scheduler.DEFAULT_GROUP);
        CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);

        //若是定時任務開啓,從數據庫讀取該job的執行時間進行比較
        //若是相關不作任何處理,若是不等則更新數據庫中的執行時間
        String oldTime = trigger.getCronExpression();
        if (!oldTime.equals(cron)) {
            logger.info("quartz job's oldTime not equals config cron, trigger name is : " + triggerName
                + "|oldTime is : " + oldTime
                + "|config cron is : " + cron);
            //觸發器
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
            //觸發其名,觸發器組
            triggerBuilder.withIdentity(triggerName, Scheduler.DEFAULT_GROUP);
            triggerBuilder.startNow();
            //觸發器時間設定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
            //建立Trigger對象
            trigger = (CronTrigger) triggerBuilder.build();
            //修改一個任務的觸發時間
            sched.rescheduleJob(triggerKey, trigger);
            logger.info("update quartz job:" + trigger.getJobKey().getName() + " succ.........");
        } else {
            logger.info("the quartz job's config cron is equals old cron, do nothing for this quartz: " + triggerName);
        }
    }

    /**
     * 讀取配置文件
     * @return
     */
    private Properties getProperty() {
        try {

            // 先獲取jvm傳參
            String pathRoot = "D:\\Test\\";
            String path = pathRoot + "quartz-config.properties";
            if ("".equals(pathRoot)){
                path = QuartzScheduleConfigTask.class.getClassLoader().getResource("quartz-config.properties").getPath();
            }
            Resource resource = new FileSystemResource(path);
            properties = PropertiesLoaderUtils.loadProperties(resource);
            Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
            for (Map.Entry<Object, Object> entry : entrySet) {
                String decryptValue = EncryptPropertyPlaceholderConfigurer
                    .getDecryptPara(entry.getKey().toString(), entry.getValue().toString());
                properties.setProperty(entry.getKey().toString(), decryptValue);
            }

            return properties;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }

        return null;
    }
}
相關文章
相關標籤/搜索