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("运行一次定时任务失败");
        }
    }

}


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




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

Quartz集成springMVC 的方案二(持久化任务、集群和分布式)

Quartz是一个开放源码项目,专注于任务调度器,提供了极为广泛的特性如持久化任务,集群和分布式任务等。 Quartz核心是调度器,还采用多线程管理。   1.持久化任务:当应用程序停止运行时,所有调...
  • congcong68
  • congcong68
  • 2014年09月13日 22:29
  • 17325

Quartz与Spring结合使用及集群配置

quartz介绍quartz是进行任务调度执行的框架,相对于Java中线程池调度以及Spring自带注解的调度方法,有以下几个有点: 1. 能够支持上千上万个调度任务的执行 2. 任务调度方式较为...
  • benjaminlee1
  • benjaminlee1
  • 2017年06月10日 17:04
  • 1270

Quartz任务调度[Spring+Quartz结合]_实现任务的动态添加、修改和删除

项目框架图 下面开始贴代码了,不过先贴数据库^^ -- Create table Oracle数据库 create table QUARTZ_SCHEDULEJOB ( id ...
  • YangRunkangBla
  • YangRunkangBla
  • 2016年05月22日 00:17
  • 10666

任务调度开源框架Quartz动态添加、修改和删除定时任务

Quartz 是个开源的作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。Quartz框架包含了调度器监听、作业和触发器监听。你可以配置作业和触发器监听为全局监听或者是特定于...
  • pengpegV5yaya
  • pengpegV5yaya
  • 2014年07月09日 12:24
  • 78788

quartz框架实现动态添加任务——细节问题

scheduler的注入,网上许多文章提到的xml配置为: 实际上这样的配置在正常情况下是没有问题的,但是如果你的项目中还用到了spring配置的bean,并且该bean需要在quartz的业务...
  • smallpig88
  • smallpig88
  • 2016年11月28日 20:19
  • 1187

quartz集群动态配置时间

项目开发工作中会遇到各种定时调度的业务开发。当服务器是集群时那么就会出现问题了。所有集群中服务器都会执行定时调度。 又或者定时调度的执行时间是不可控的。需要动态配置。那么就可以用到quartz集群以下...
  • GeekSnow
  • GeekSnow
  • 2017年08月21日 15:45
  • 224

动态创建quartz作业

动态创建quartz作业最近在公司的业务中遇到了需要动态创建quartz作业的需求(在定时推送任务创建后,还可以更改任务的执行方法、时间、参数等),这对于之前没用过quartz的我来说十分蛋疼。但在捣...
  • coki4568213
  • coki4568213
  • 2017年05月15日 16:03
  • 285

最新 Spring 4.2.2 集成 Quartz Scheduler 2.2.2 任务调度示例

本文将演示如何通过 Spring 使用 Quartz Scheduler 进行任务调度。Spring 为简化 Quartz 的操作提供了相关支持类。本文示例使用的相关工具如下:Spring 4.2.2...
  • defonds
  • defonds
  • 2015年10月29日 21:20
  • 47627

spring4和quartz2.x整合,对任务做动态操作(增删改查)

quartz 1.x版本和quartz2.x版本变化有点大,所以开始使用quartz时要注意版本 项目结构如下 本demo 的设计思想是对定时任务的增删改查等,需要通过相关的反射知识,及通过任务描...
  • zhaoyachao123
  • zhaoyachao123
  • 2017年10月15日 16:12
  • 730

Spring4实战(二)-Quartz 2.2 集成

前面一篇文章介绍了 Quartz的简单用法,Spring提供了一些类来简化Quartz的使用。本篇文章将介绍如何在Spring中集成Quartz。涉及的开发工具与技术 Spring 4.2.4.REL...
  • FX_SKY
  • FX_SKY
  • 2016年01月05日 19:34
  • 2829
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Spring4+Quartz2集群动态创建任务
举报原因:
原因补充:

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