Spring4+Quartz2集群动态创建任务

原创 2016年08月29日 18:32:30

     公司最近需要使用Quartz集群来实现任务的动态创建和删除,之前自己只是用过配置好的单机版的,而且是定时

执行的任务,正好借这个机会深入学习一下Quartz。

     在正式开始之前,我们先来了解下,spring3.1以下的版本必须使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然会出错。至于原因,则是spring对于quartz的支持实现,org.springframework.scheduling.quartz.CronTriggerBean继承了org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是个类,而在quartz2.x系列中org.quartz.CronTrigger变成了接口,从而造成无法用spring的方式配置quartz的触发器(trigger)。公司现运行项目用的spring版本是4.2.2.RELEASE,所有我选取的quartz版本是2.2.1。

     最终实现的功能:

     1)项目启动时,可动态添加、删除、修改和执行定时任务;

     2)因为是集群,一个实例运行挂了,保存在数据库的定时任务可以继续执行;而且定时任务只能被其中一个实例执行。


    一、引入Maven坐标


		<!-- quartz任务调度 -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.2.1</version>
		</dependency>


     二、创建数据库表


     在Quartz包下docs/dbTables,选择对应的数据库脚本,创建相应的数据库表即可,我用的是mysql5.6,这里有一个需要注意的地方,mysql5.5之前用的表存储引擎是MyISAM,使用的是表级锁,锁发生冲突的概率比较高,并发度低;5.6之后默认的存储引擎为InnoDB,InnoDB采用的锁机制是行级锁,并发度也较高。而quartz集群使用数据库锁的

机制来来实现同一个任务在同一个时刻只被实例执行,所以为了防止冲突,我们建表的时候要选取InnoDB作为表的存

储引擎。如下:


      


     三、配置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" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd  
    http://www.springframework.org/schema/context     
    http://www.springframework.org/schema/context/spring-context-4.1.xsd   
    http://www.springframework.org/schema/task    
    http://www.springframework.org/schema/task/spring-task-4.1.xsd">


	<!-- quartz持久化存储 -->
	<bean name="quartzScheduler"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
		<property name="applicationContextSchedulerContextKey" value="applicationContext" />
		<!--也可以在quartz.properties中配置-->
		<property name="quartzProperties">
			<props>
				<prop key="org.quartz.scheduler.instanceName">CRMscheduler</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">20</prop>
				<prop key="org.quartz.threadPool.threadPriority">5</prop>
				<prop key="org.quartz.jobStore.misfireThreshold">120000</prop>
				<!-- JobStore 配置 -->
				<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
				<!-- 集群配置 -->
				<prop key="org.quartz.jobStore.isClustered">true</prop>
				<prop key="org.quartz.jobStore.clusterCheckinInterval">15000</prop>
				<prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">1</prop>
				<!-- 数据表设置 -->
				<prop key="org.quartz.jobStore.tablePrefix">qrtz_</prop>
				<prop key="org.quartz.jobStore.dataSource">qzDS</prop>
			</props>
		</property>

		<!--可选,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
		<property name="overwriteExistingJobs" value="true" />
		
		<!--设置自动启动 -->
		<property name="autoStartup" value="true" />

	</bean>

</beans>

     引入spring配置文件:

<import resource="quartz.xml"/>


     配置文件中SchedulerFactoryBean中的dataSource用指定spring中之前配置好的就可以。


     四、实现Job接口


     我们在动态添加任务的时候需要用到这个类。


public class AllPushNotifyJob implements Job {
    private static final Log logger = LogFactory.getLog(AllPushNotifyJob.class);

    private AllPushMessageService allPushMessageService;

    public AllPushNotifyJob() {
        allPushMessageService = (AllPushMessageService) SpringContext.getBeanByName("allPushMessageService");
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDataMap dataMap = jobExecutionContext.getJobDetail().getJobDataMap();

        AllPushMessage  allPushMessage = (AllPushMessage) dataMap.get("allPushMessage");

        Date expectTriggerTime = allPushMessage.getPush_time();

        Date realTriggerTime = new Date();
        logger.info("execute category notify job with expect trigger time:" + DateUtils.format(expectTriggerTime, "yyyy-MM-dd HH:mm:ss Z"));
        logger.info("real notify time:" + DateUtils.format(realTriggerTime, "yyyy-MM-dd HH:mm:ss Z"));
        allPushMessageService.enforceAllPush(allPushMessage);
    }
}


     五、JobScheduler管理任务


@Service
public class AllPushJobScheduler {
    private static final Log logger = LogFactory.getLog(AllPushJobScheduler.class);

    public void start() {
        logger.info("start category update notify scheduler");
        schedulerFactoryBean.start();

    }

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    /**
     * 添加任务
     * @param allPushMessage
     */
	public void scheduleNotifyJob(AllPushMessage allPushMessage) {
		if (allPushMessage.getPush_time().compareTo(new Date()) < 0) {
			allPushMessage.setPush_time(DateUtils.addSeconds(new Date(), 10));
		}

		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = getJobKey(allPushMessage);
		try {
			if (scheduler.checkExists(jobKey)) {
				logger.info("all push job existed!:" + jobKey.getName());
				return;
			}
		} catch (SchedulerException e) {
			logger.error("get exception:" + e.getMessage(), e);
		}

		logger.info("schedule all push job at:"+allPushMessage.getPush_time() +" with job name pushID" + jobKey.getName());
		JobDataMap jobData = new JobDataMap();
		jobData.put("allPushMessage", allPushMessage);

		JobDetail notifyJob = JobBuilder.newJob(AllPushNotifyJob.class)
				.setJobData(jobData).withIdentity(jobKey).build();
		SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
		trigger.setName("trigger-" + jobKey.getName());
		trigger.setJobDetail(notifyJob);
		trigger.setStartTime(allPushMessage.getPush_time());
		trigger.setRepeatCount(0);
		trigger.afterPropertiesSet();

		try {
			schedulerFactoryBean.getScheduler().scheduleJob(notifyJob,
					trigger.getObject());
		} catch (SchedulerException e) {
			logger.error("get exception when executing quartz job" + e);
		}

	}


    /**
     * 删除任务
     * @param allPushMessage
     * @throws Exception
     */
    public void deleteJob(AllPushMessage allPushMessage)throws Exception{

        try{
            //删除定时任务时   先暂停任务,然后再删除
            JobKey jobKey = getJobKey(allPushMessage);
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            scheduler.pauseJob(jobKey);
            scheduler.deleteJob(jobKey);
        }catch(Exception e){
            System.out.println("删除定时任务失败"+e);
            throw new Exception("删除定时任务失败");
        }
    }


    /**
     * 获取jobKey
     * @param allPushMessage
     * @return
     */
    public JobKey getJobKey(AllPushMessage allPushMessage) {

        return JobKey.jobKey(String.valueOf(allPushMessage.getPush_id()));
    }

    /**
     * 更新定时任务
     * @param
     * @param
     * @throws Exception
     */
    public void updateJob(AllPushMessage allPushMessage)throws Exception{
        try {
            TriggerKey triggerKey =getTriggerKey(String.valueOf(allPushMessage.getPush_id()));
            Scheduler scheduler = schedulerFactoryBean.getScheduler();

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).build();

            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            System.out.println("更新定时任务失败"+e);
            throw new Exception("更新定时任务失败");
        }
    }

    /**
     * 获取触发器key
     *
     * @param
     * @param
     * @return
     */
    public static TriggerKey getTriggerKey(String jobkey) {

        return TriggerKey.triggerKey(jobkey);
    }



    /**
     * 暂停定时任务
     * @param allPushMessage
     * @throws Exception
     */
    public void pauseJob(AllPushMessage allPushMessage) throws Exception {

        JobKey jobKey = JobKey.jobKey(String.valueOf(allPushMessage.getPush_id()));
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("暂停定时任务失败"+e);
            throw new Exception("暂停定时任务失败");
        }
    }

    /**
     * 恢复任务
     * @param
     * @param
     * @param
     * @throws Exception
     */
    public void resumeJob(AllPushMessage allPushMessage) throws Exception {

        JobKey jobKey = JobKey.jobKey(String.valueOf(allPushMessage.getPush_id()));
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("恢复定时任务失败"+e);
            throw new Exception("恢复定时任务失败");
        }
    }

    /**
     * 运行一次任务
     * @param allPushMessage
     * @throws Exception
     */
    public void runOnce(AllPushMessage allPushMessage) throws Exception {
        JobKey jobKey = JobKey.jobKey(String.valueOf(allPushMessage.getPush_id()));
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("运行任务失败"+e);
            throw new Exception("运行一次定时任务失败");
        }
    }

}


     最后,在需要你的代码中调用添加、删除、修改任务的方法就可以了,喵




版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Spring4+Quartz2计划任务集群

项目中经常需要计划任务来定时处理一些事情,Quartz是javaEE项目最常见的计划任务组件,Quartz本身支持集群,和spring结合很容易实现计划任务的配置,但是默认spring的Quartz配...

Spring4.x整合Quartz2.x动态修改定时器时间(修正多定时器SpringBean异常)

Spring4.x整合Quartz2.x 动态修改定时器时间(修正多定时器SpringBean异常) 前不久在工作中遇到动态修改定时器的问题,在网上找了一些例子,大部分的写法都差不多,看完过后应用到...

Spring4 Quartz2 动态任务,Spring4整合quartz2.2.3简单动态任务

Spring4 Quartz2 动态任务 Spring4整合quartz2.2.3简单动态任务, Quartz2.2.3简单动态定时任务二, SimpleScheduleBuilder简单定时任...

spring3.2.0 quartz-2.2.1 整合 实现动态定时任务

spring3.2.0和quzrtz2.2.1 的整合 实现定时任务的的动态加载,以及实现 quartz 中的 job自动注入spring容器托管的对象

quartz2.2.1和spring4.1.4整合使用

quartz2.2.1和spring4.1.4整合使用

Spring 3整合Quartz 2实现定时任务二:动态添加任务

前面,我们已经对Spring 3和Quartz 2用配置文件的方式进行了整合,如果需求比较简单的话应该已经可以满足了。但是很多时候,我们常常会遇到需要动态的添加或修改任务,而spring中所提供的定时...

spring4 + quartz2初探

1.准备工作,构建一个maven web工程 pom.xml如下:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http...

spring3.X集成quartz2.X实现动态定时器

1.首先在Spring xml里定义 2.设置po类public class TScheduleJob implements java.io.Serializable { // F...

Quartz入门实例13-spirng4和quartz2实现动态任务调用

Quartz入门实例13-spirng4和quartz2实现动态任务调用
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)