手记

springBoot2 结合 Quartz

为什么使用Quartz

定时任务是框架不可缺少的一部分,spring自带的定时任务工具,已经可以满足使用,但无法满足分布式的情况(实际也可以用分布式锁来实现),所以使用Quartz来实现框架的定时器功能

1.Quartz的一般使用

一般框架下怎么使用
1.1 引入jar

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

1.2 定义一个Job,具体执行任务的类

@DisallowConcurrentExecution
public class HelloJob implements Job{
	
	private static Logger log = LogManager.getLogger();

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		log.info(" HelloJob ");
	}
}

1.3 定义jobDetail,trigger,Scheduler
job与jobDetail绑定,形成任务
trigger定义了任务的执行时间
Scheduler 执行一对任务与触发器

public class HelloScheduler {
	public static void main(String[] args) throws SchedulerException, ParseException {
		// jobdetail 和 job实例绑定
		JobDetail helloJobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "helloGroup").build();
		
		// 触发器 simpleTrigger(SimpleScheduleBuilder)  CronTrigger	
		// 每5秒运行一次	
		Trigger trigger = TriggerBuilder.newTrigger().withIdentity("helloTrigger", "helloGroup")
				.withSchedule(CronScheduleBuilder.cronSchedule("/5 * * * * ? *"))
				.build();
		
		// 调度器
		SchedulerFactory schedulerFactory = new StdSchedulerFactory();
		Scheduler scheduler = schedulerFactory.getScheduler();
		scheduler.start();
		//执行任务
		scheduler.scheduleJob(helloJobDetail, trigger);
	}
}

2. springBoot2 结合 Quartz

2.1 jar引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2.2 QuartzConfiguration

public class QuartzJobVo {
	
	//任务信息
	private String jobName;
	private String jobGroupName;
	
	//触发器信息
	private String triggerName;
	private String triggerGroupName;
	
	private String cron;

	//具体任务类
	private Class<? extends Job> jobClass;
	//需要被删除
	private boolean needDelete = false;

	/**
	 * Method name: QuartzJobVo<BR>
	 * Description: please write your description<BR>
	 * Remark: <BR> <BR>
	 */
	public QuartzJobVo() {
		super();
	}

	public QuartzJobVo(String jobName, String jobGroupName, String triggerName, String triggerGroupName, 
			Class<? extends Job> jobClass, String cron) {
		super();
		this.jobName = jobName;
		this.jobGroupName = jobGroupName;
		this.triggerName = triggerName;
		this.triggerGroupName = triggerGroupName;
		this.cron = cron;
		this.jobClass = jobClass;
	}

	public QuartzJobVo(String jobName, String jobGroupName, String triggerName, String triggerGroupName,
			Class<? extends Job> jobClass, String cron, boolean needDelete) {
		super();
		this.jobName = jobName;
		this.jobGroupName = jobGroupName;
		this.triggerName = triggerName;
		this.triggerGroupName = triggerGroupName;
		this.cron = cron;
		this.jobClass = jobClass;
		this.needDelete = needDelete;
	}
...................
	}
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class HelloJob extends QuartzJobBean{
	
	private static Logger log = LogManager.getLogger();

	@Override
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
		// 任务具体执行的地方
		log.info("Hello world!:" + context.getJobDetail().getKey());
	}
}
@Configuration
public class QuartzConfiguration {
	
	@Autowired
    private DataSource dataSource;
	
	private static Logger log = LogManager.getLogger();
	
	//任务集合
	private final QuartzJobVo[] Jobs = {
		new QuartzJobVo("hello2", "hello2", "hello2", "hello2", HelloJob.class, "/5 * * * * ? *"),
		new QuartzJobVo("hello", "hello", "hello", "hello", HelloJob.class, "4,14 * * * * ? *", true)
	};
	
	@Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);

        //quartz参数
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "WIFIScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        prop.put("org.quartz.scheduler.skipUpdateCheck", true);
        //线程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "10");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        //JobStore配置
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
        //集群配置
        prop.put("org.quartz.jobStore.isClustered", "true");
        //#设置此实例“检入”*与群集的其他实例的频率(以毫秒为单位)。影响检测失败实例的速度。
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
        // #在被认为“失火”之前,调度程序将“容忍”一个Triggers将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");

        //PostgreSQL数据库,需要打开此注释
        //prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");

        factory.setQuartzProperties(prop);

        factory.setSchedulerName("WIFIScheduler");
        //延时启动
        factory.setStartupDelay(30);
        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
        //设置自动启动,默认为true
        factory.setAutoStartup(true);
        //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        //实际运用中,要把jobDetail,trigger 全都交给spring管理,这个属性才起作用
        factory.setOverwriteExistingJobs(true);
		
        JobDetail[] jobDetails = new JobDetail[Jobs.length];
        Trigger[] triggers = new Trigger[Jobs.length];
        
        int size = 0;
		for (QuartzJobVo vo : Jobs) {
		  
			//job定义: // 任务名,任务组,任务执行类 
			JobDetail jobDetail = JobBuilder.newJob(vo.getJobClass()) 
									.withIdentity(vo.getJobName(), vo.getJobGroupName()) 
									.storeDurably() 
									.build();
			jobDetails[size] = jobDetail;
		  
			//触发器构建 
			Trigger trigger = TriggerBuilder.newTrigger()
								.withIdentity(vo.getTriggerName(), vo.getTriggerGroupName())
								.withSchedule(CronScheduleBuilder.cronSchedule(vo.getCron()))
								.forJob(jobDetail) 
								.build();
			triggers[size] = trigger;
		  
			log.info("增加了定时任务 vo={}", JSON.toJSONString(vo)); 
			size++;
		}
		
		factory.setJobDetails(jobDetails);
		factory.setTriggers(triggers);
		
        return factory;
    }
	
    @Bean
    public void deleteJob() {
    	
    	Scheduler scheduler = schedulerFactoryBean().getScheduler();
    	
    	for (QuartzJobVo vo : Jobs) {
    		
    		if(vo.isNeedDelete()) {
    			//需要被删除
    			JobKey jobKey = JobKey.jobKey(vo.getJobName(), vo.getJobGroupName()); 
    			
    			if(jobKey !=null) {
    				try {
    					scheduler.deleteJob(jobKey); 
    					log.info("定时任务删除成功  vo={}", JSON.toJSONString(vo));
    				} catch (Exception e) {
    					log.error("定时任务删除出错  vo={}", JSON.toJSONString(vo), e);
    					
    					try {
    						scheduler.pauseJob(jobKey);
						} catch (Exception e2) {
							log.error("定时任务暂停出错  vo={}", JSON.toJSONString(vo), e);
						}
    				}
    			}else {
    				log.info("定时任务已经不存在quartz表中 vo={}", JSON.toJSONString(vo));
    			}
    		}
    	}
    }
}

3. 注意

3.1 @DisallowConcurrentExecution,将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例,即同一个job,即是时间到了,只要上一个还在运行,新的就等待,spring的定时默认有这个机制
3.2 @PersistJobDataAfterExecution,将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据
3.3 线程池,org.quartz.threadPool.threadCount,默认10个,每一个在运行的job会占用一个线程,线程池被用完,新的任务只能排队

0人推荐
随时随地看视频
慕课网APP