• quartz集群 定时任务 改成可配置


    前面的博文中提到的quartz集群方式会有以下缺点:

    1.假设配置了3个定时任务,job1,job2,job3,这时数据库里会有3条job相关的记录,如果下次上线要停掉一个定时任务job1,那即使定时任务配置文件 quartz.xml 中的trigger 去掉了,数据库还是会有该条记录,若代码没有去掉,那定时任务还是会执行。

    ------解决方法1:修改该trigger的触发时间,比如年改成2099,让它不触发,(SchedulerFactoryBean中配置了  <property name="overwriteExistingJobs" value="true" />),会覆盖数据库中的相关记录。

    缺点:曲线救国策略,不够好。

    ------解决方法2:删除数据库中该job的相关记录。

    缺点:要操作数据库,而且表中数据还有外键约束,操作起来麻烦,还可能不小心破坏了其他job信息。

    解决方法3:

    介绍一种用配置文件的方式,通过开关控制,达到更细粒度的控制。主要是通过Scheduler的相关方法:Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。Scheduler包含了很多方法,添加、执行一次、恢复全部、删掉任务暂停全部等方法,方便用户灵活使用。

    1.创建定时任务的配置文件 quartz-config.properties

    #定时任务配置文件
    #key:
    # CronTriggerFactoryBean配置的id
    # switch:定时任务开关,ON:开启该定时任务; OFF:关闭该定时任务
    # cron:该定时任务的执行时间间隔(cron表达式)

    #定时任务(每隔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;
        }
    }
  • 相关阅读:
    jquery事件优化---事件委托
    2017年7月6号,总结所遇到的问题
    js日期函数
    跨域请求所遇到的错误
    ajax设置Access-Control-Allow-Origin实现跨域访问
    php提前输出响应及注意问题
    php中的日期和时间
    跨域请求json数据
    C++ 与 Visual Studio 2019 和 WSL(四)——库组件
    fpic 和 fPIC
  • 原文地址:https://www.cnblogs.com/wuyun-blog/p/8617830.html
Copyright © 2020-2023  润新知