quartz 分布式_老板今天让我搞 分布式定时任务....

作者 | 荒野猛兽

来源 | urlify.cn/vqiYva

第一步:引入依赖

                    org.quartz-scheduler            quartz            ${quartz.version}                            org.quartz-scheduler            quartz-jobs            ${quartz.version}                                    org.springframework            spring-context-support        

第二步:创建MySQL表,Quartz是基于表来感知其他定时任务节点的,节点间不会直接通信。建表语句在jar包中自带了。

orgquartz-schedulerquartz2.3.0quartz-2.3.0.jar!orgquartzimpljdbcjobstoreables_mysql_innodb.sql

6e3c707b3fee8600ee7608dec0a6931b.png

第三步:配置线程池,我这里是因为项目的其他地方有用到线程池,你也可以选择在Quartz的配置类中注入。

(我在其他位置使用了线程池,占用了一个线程,所以当我将核心线程数量设置为1时,定时任务不会执行;需确保有足够的线程来执行)

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;import java.util.concurrent.ThreadPoolExecutor;/** * @Author 1 * @Description 配置线程池交给Spring容器管理 * @Date 2020/8/26 18:23 **/@Configurationpublic class ExecturConfig {    @Bean("taskExector")    public Executor taskExector() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        //核心线程池数量        executor.setCorePoolSize(2);        //最大线程数量        executor.setMaxPoolSize(5);        //线程池的队列容量        executor.setQueueCapacity(10);        //线程名称的前缀        executor.setThreadNamePrefix("expireOrderHandle-");        //配置拒绝策略        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());        executor.initialize();        return executor;    }}

第四步:因为定时任务业务中需要使用到注入Spring容器的类,所以配置注入,否则报空指针异常。

参考了一位大佬的博客:https://blog.csdn.net/qq_39513430/article/details/104996237

import org.quartz.spi.TriggerFiredBundle;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.config.AutowireCapableBeanFactory;import org.springframework.scheduling.quartz.AdaptableJobFactory;import org.springframework.stereotype.Component;@Component("myAdaptableJobFactory")public class MyAdaptableJobFactory extends AdaptableJobFactory {    //AutowireCapableBeanFactory 可以将一个对象添加到SpringIOC容器中,并且完成该对象注入    @Autowired    private AutowireCapableBeanFactory autowireCapableBeanFactory;        /**     * 该方法需要将实例化的任务对象手动的添加到springIOC容器中并且完成对象的注入     */    @Override    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {        Object obj = super.createJobInstance(bundle);        //将obj对象添加Spring IOC容器中,并完成注入        this.autowireCapableBeanFactory.autowireBean(obj);        return obj;    }}

第五步:添加Quartz属性文件

关于属性配置解释参考https://blog.csdn.net/github_36429631/article/details/63254055

#============================================================================# Configure JobStore# Using Spring datasource in SchedulerConfig.java# Spring uses LocalDataSourceJobStore extension of JobStoreCMT#============================================================================#设置为TRUE不会出现序列化非字符串类到 BLOB 时产生的类版本问题org.quartz.jobStore.useProperties=true#quartz相关数据表前缀名org.quartz.jobStore.tablePrefix = QRTZ_#开启分布式部署org.quartz.jobStore.isClustered = true#分布式节点有效性检查时间间隔,单位:毫秒org.quartz.jobStore.clusterCheckinInterval = 20000#信息保存时间 默认值60秒org.quartz.jobStore.misfireThreshold = 60000#事务隔离级别为“读已提交”org.quartz.jobStore.txIsolationLevelReadCommitted = true#配置线程池实现类org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate#============================================================================# Configure Main Scheduler Properties# Needed to manage cluster instances#============================================================================org.quartz.scheduler.instanceName = ClusterQuartzorg.quartz.scheduler.instanceId= AUTO#如果你想quartz-scheduler出口本身通过RMI作为服务器,然后设置“出口”标志true(默认值为false)。org.quartz.scheduler.rmi.export = false#true:链接远程服务调度(客户端),这个也要指定registryhost和registryport,默认为false# 如果export和proxy同时指定为true,则export的设置将被忽略org.quartz.scheduler.rmi.proxy = falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction = false#============================================================================# Configure ThreadPool# Can also be configured in spring configuration#============================================================================#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)#org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool#org.quartz.threadPool.threadCount = 5#org.quartz.threadPool.threadPriority = 5#org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

第六步:写任务类

package com.website.task;import com.website.mapper.WebTeacherMapper;import com.website.pojo.ao.TeacherSalaryRuleAO;import com.website.pojo.bo.TeacherSalaryRuleDetailBO;import com.website.pojo.bo.TeacherSalaryRuleRelationBO;import com.website.pojo.bo.TeacherSalaryStatTempBO;import com.website.pojo.bo.TeacherSalaryStatisticBO;import io.jsonwebtoken.lang.Collections;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import org.joda.time.DateTime;import org.quartz.DisallowConcurrentExecution;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.quartz.PersistJobDataAfterExecution;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.quartz.QuartzJobBean;import java.math.BigDecimal;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.stream.Collectors;@PersistJobDataAfterExecution@DisallowConcurrentExecution@Slf4jpublic class QuartzJob extends QuartzJobBean {    @Autowired    private WebTeacherMapper webTeacherMapper;    @Override    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {        log.info("统计老师月薪资定时任务开始执行。");        System.out.println("任务编写位置");        log.info("统计老师月薪资定时任务执行完毕。");    }}

第七步:配置定时器

import com.website.task.QuartzJob;import org.quartz.Scheduler;import org.quartz.TriggerKey;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.config.PropertiesFactoryBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.scheduling.quartz.CronTriggerFactoryBean;import org.springframework.scheduling.quartz.JobDetailFactoryBean;import org.springframework.scheduling.quartz.SchedulerFactoryBean;import javax.sql.DataSource;import java.io.IOException;import java.util.Properties;import java.util.concurrent.Executor;@Configurationpublic class SchedulerConfig {    @Autowired    private DataSource dataSource;    @Autowired    private Executor taskExector;    @Autowired    private MyAdaptableJobFactory myAdaptableJobFactory;    @Bean    public Scheduler scheduler() throws Exception {        Scheduler scheduler = schedulerFactoryBean().getScheduler();        TriggerKey triggerKey1 = TriggerKey.triggerKey("trigger1", "TriggerTest111");        /*========如果有必要可以配置删除任务,开始====================*/        //停止触发器//        scheduler.pauseTrigger(triggerKey1);        //移除触发器//        scheduler.unscheduleJob(triggerKey1);//        JobKey jobKey1 = JobKey.jobKey("job1111------", "quartzTest--------");        //删除任务//        boolean b = scheduler.deleteJob(jobKey1);//        System.out.println(b);        /*=========结束====================*/        scheduler.start();        return scheduler;    }    @Bean    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {        SchedulerFactoryBean factory = new SchedulerFactoryBean();        //开启更新job        factory.setOverwriteExistingJobs(true);        //如果不配置就会使用quartz.properties中的instanceName        //factory.setSchedulerName("Cluster_Scheduler");        //配置数据源,这是quartz使用的表的数据库存放位置        factory.setDataSource(dataSource);        //设置实例在spring容器中的key        factory.setApplicationContextSchedulerContextKey("applicationContext");        //配置线程池        factory.setTaskExecutor(taskExector);        //配置配置文件        factory.setQuartzProperties(quartzProperties());        //设置调度器自动运行        factory.setAutoStartup(true);        //配置任务执行规则,参数是一个可变数组        factory.setTriggers(trigger1().getObject());        // 解决mapper无法注入问题,此处配合第四步的配置。        factory.setJobFactory(myAdaptableJobFactory);        return factory;    }    @Bean    public Properties quartzProperties() throws IOException {        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));        // 在quartz.properties中的属性被读取并注入后再初始化对象        propertiesFactoryBean.afterPropertiesSet();        return propertiesFactoryBean.getObject();    }    @Bean    public JobDetailFactoryBean job1() {        JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();        //配置任务的具体实现        jobDetail.setJobClass(QuartzJob.class);        //是否持久化        jobDetail.setDurability(true);        //出现异常是否重新执行        jobDetail.setRequestsRecovery(true);        //配置定时任务信息        jobDetail.setName("TeacherSalaryJob");        jobDetail.setGroup("TeacherSalaryJobGroup");        jobDetail.setDescription("这是每月1号凌晨统计教师薪资任务");        return jobDetail;    }    @Bean    public CronTriggerFactoryBean trigger1() {        CronTriggerFactoryBean cronTrigger = new CronTriggerFactoryBean();        //定时规则的分组        cronTrigger.setGroup("TeacherSalaryTriggerGroup");        cronTrigger.setName("TeacherSalaryTrigger");        //配置执行的任务jobdetail        cronTrigger.setJobDetail(job1().getObject());        //配置执行规则 每月一号0点过1分执行一次        cronTrigger.setCronExpression("0 1 0 1 * ? ");        return cronTrigger;    }}

到此完毕,另外发现如果执行任务的代码中报错,会导致定时任务停止循环,重启也不会再执行。建议任务内容用try...catch代码块包裹起来,打印好日志。

已中断的任务清空Quartz所有表格,再启动项目即可再次触发启动任务。

如果某一天定时任务突然不执行了,网上很多情况都是远程调用没有加超时中断,从而导致线程阻塞引起的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值