Quartz集群配置

quartz的介绍就不说了,这里主要说的是使用spring boot+ 2.2.1搭建集群。

1.pom引入


2.在resources下新建quartz.properties文件,用于覆盖默认配置。

这里是集群配置文件

#quartz集群配置

# ===========================================================================

# Configure Main Scheduler Properties 调度器属性

# ===========================================================================

#调度标识名 集群中每一个实例都必须使用相同的名称

org.quartz.scheduler.instanceName=DefaultQuartzScheduler

#ID设置为自动获取 每一个必须不同

org.quartz.scheduler.instanceid=AUTO

#禁用quartz软件更新

org.quartz.scheduler.skipUpdateCheck=true

#============================================================================

# Configure ThreadPool

#============================================================================

#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool

#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)

org.quartz.threadPool.threadCount=25

#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)

org.quartz.threadPool.threadPriority=5

#============================================================================

# Configure JobStore

#============================================================================

# 信息保存时间 默认值60秒

org.quartz.jobStore.misfireThreshold=60000

#数据保存方式为数据库持久化

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX

#数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库

org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate

#JobDataMaps是否都为String类型

org.quartz.jobStore.useProperties=false

#数据库别名 随便取

org.quartz.jobStore.dataSource=myDS

#表的前缀,默认QRTZ_

org.quartz.jobStore.tablePrefix=QRTZ_

#是否加入集群

org.quartz.jobStore.isClustered=true

#调度实例失效的检查时间间隔

org.quartz.jobStore.clusterCheckinInterval=20000

#============================================================================

# Configure Datasources

#============================================================================

#数据库引擎

org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver

#数据库连接

org.quartz.dataSource.myDS.URL=jdbc:mysql://127.0.0.1:3306/dbapp_dandan?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true

#数据库用户

org.quartz.dataSource.myDS.user=root

#数据库密码

org.quartz.dataSource.myDS.password=

#允许最大连接

org.quartz.dataSource.myDS.maxConnections=5

#验证查询sql,可以不设置

org.quartz.dataSource.myDS.validationQuery=select 0 from dual

3.运行数据库脚本

解压官网下载下来的包,如果你的数据库是mysql,用这个tables_mysql_innodb.sql,里面有11个表



4下面是集群两种使用实现方式

第一种 

1.添加任务注入的工厂类,否则任务注入会出错:

/**

* Created by Administrator on 2018\6\9 0009.

*/

public class DetailQuartzJobBeanextends QuartzJobBean {

protected final Loglogger = LogFactory.getLog(getClass());

private StringtargetObject;

private StringtargetMethod;

private ApplicationContextctx;

@Override

    protected void executeInternal(JobExecutionContext context)throws JobExecutionException {

try {

Object otargetObject =ctx.getBean(targetObject);

Method m =null;

try {

m = otargetObject.getClass().getMethod(targetMethod,new Class[] { JobExecutionContext.class });

m.invoke(otargetObject,new Object[] { context });

}catch (SecurityException e) {

logger.error(e);

}catch (NoSuchMethodException e) {

logger.error(e);

}

}catch (Exception e) {

throw new JobExecutionException(e);

}

}

public void setApplicationContext(ApplicationContext applicationContext) {

this.ctx = applicationContext;

}

public void setTargetObject(String targetObject) {

this.targetObject = targetObject;

}

public void setTargetMethod(String targetMethod) {

this.targetMethod = targetMethod;

}

}


2.配置quartz


@Configuration

public class QuartzConfig {

// 配置文件路径

    static final StringQUARTZ_CONFIG ="/quartz.properties";

/**

* Description: 定义调用对象和调用对象的方法

*

    * @param

    * @return

    * @see

    */

    @Bean(name ="initJobDetail")

public JobDetailFactoryBean enjoyQuartzJobTask()

{

//集群模式下必须使用JobDetailFactoryBean, MethodInvokingJobDetailFactoryBean 类中的 methodInvoking 方法,是不支持序列化的

        JobDetailFactoryBean bean =new JobDetailFactoryBean();

bean.setName("enjoyQuartzJob");// 设置任务的名字

        bean.setGroup("enjoyQuartzJobGroup");// 设置任务的分组,这些属性都可以存储在数据库中,在多任务的时候使用

        bean.setDurability(true);

bean.setRequestsRecovery(true);

bean.setJobClass(DetailQuartzJobBean.class);

Map map =new HashMap<>();

map.put("targetObject","initBjLu28Session");//任务所在的类

        map.put("targetMethod","task");//具体执行任务的方法

        bean.setJobDataAsMap(map);

return bean;

}

// 配置触发器2

    @Bean(name ="initJobTrigger")

public CronTriggerFactoryBean secondTrigger(JobDetail initJobDetail) {

CronTriggerFactoryBean trigger =new CronTriggerFactoryBean();

trigger.setJobDetail(initJobDetail);

// cron表达式

        trigger.setCronExpression("0 0/1 * * * ?");

return trigger;

}

// 配置定时任务3

    @Bean(name ="fetResultJobDetail")

public MethodInvokingJobDetailFactoryBean thirdJobDetail(FetchResult fetResultJob) {

MethodInvokingJobDetailFactoryBean jobDetail =new MethodInvokingJobDetailFactoryBean();

// 是否并发执行

        jobDetail.setConcurrent(false);

// 为需要执行的实体类对应的对象

        jobDetail.setTargetObject(fetResultJob);

// 需要执行的方法

        jobDetail.setTargetMethod("task");

return jobDetail;

}

// 配置触发器2

    @Bean(name ="fetResultJobTrigger")

public CronTriggerFactoryBean thirdTrigger(JobDetail fetResultJobDetail) {

CronTriggerFactoryBean trigger =new CronTriggerFactoryBean();

trigger.setJobDetail(fetResultJobDetail);

// cron表达式

        trigger.setCronExpression("5 0/5 9-23 * * ?");

return trigger;

}

// 配置Scheduler

    @Bean(name ="scheduler")

public SchedulerFactoryBean schedulerFactory(Trigger initJobTrigger) {

SchedulerFactoryBean bean =new SchedulerFactoryBean();

//    public SchedulerFactoryBean schedulerFactory(Trigger initJobTrigger, Trigger fetResultJobTrigger) {

//        SchedulerFactoryBean bean = new SchedulerFactoryBean();

// 延时启动,应用启动1秒后

        bean.setStartupDelay(10);

//用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了

        bean.setOverwriteExistingJobs(true);

//用于quartz集群,加载quartz数据源

//        bean.setDataSource(dataSource);

//用于quartz集群,加载quartz数据源配置

//        bean.setQuartzProperties(quartzProperties());

//直接使用配置文件

        bean.setConfigLocation(new ClassPathResource(QUARTZ_CONFIG));

//        bean.setConfigLocation(new FileSystemResource(this.getClass().getResource("/quartz.properties").getPath()));

        bean.setApplicationContextSchedulerContextKey("applicationContext");

// 注册触发器

        bean.setTriggers(initJobTrigger);

//        bean.setTriggers(initJobTrigger,fetResultJobTrigger);

        return bean;

}

}


3.JOB任务:

@Configuration

@Component("initBjSession")

@EnableScheduling

public class InitBSessionimplements Serializable {

@Autowired

    private BjServiceImpl bjService;

//    public void task() {

    public void task(JobExecutionContext context) {

        System.out.println("-----"+ DateTimeUtil.getDateTime());

    }

}


第二种实现方式,更加简洁好用:

 * 文件名:QuartzConfig.java 版权:Copyright by www.poly.com 描述: 修改人:gogym 修改时间:2017年11月9日 跟踪单号: 修改单号:

 * 修改内容:

 */  


package com.poly.pay.configuration;  



import java.io.IOException;  

import java.text.ParseException;  

import java.util.ArrayList;  

import java.util.HashMap;  

import java.util.List;  

import java.util.Map;  

import java.util.Set;  


import org.quartz.JobDetail;  

import org.quartz.JobKey;  

import org.quartz.Scheduler;  

import org.quartz.SchedulerException;  

import org.quartz.Trigger;  

import org.quartz.TriggerKey;  

import org.quartz.impl.matchers.GroupMatcher;  

import org.quartz.impl.triggers.CronTriggerImpl;  

import org.springframework.beans.factory.annotation.Qualifier;  

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 com.poly.pay.schedule.JobRefundWeichartBean;  



@Configuration  

public class QuartzConfig  

{  


// 配置文件路径  

static final String QUARTZ_CONFIG = "properties/quartz.properties";  


// 定时任务组名称  

public static final String Quartz_Group_Name = "enjoyQuartzJobGroup";  

//定时任务方法后缀  

public static final String Quartz_Job_Suffix = "_job";  

//定时任务触发器后缀  

public static final String Quartz_Trigger_Suffix = "_trigger";  



@Bean(name = "triggers")  

public CronTriggerImpl[] createTriggers()  

throws ParseException  

    {  

List l =new ArrayList();  

l.add(createTrigger(JobRefundWeichartBean.class, "0/20 * * * * ?"));  

//l.add(createTrigger(JobRefundWeichartBean.class, "0/20 * * * * ?"));  

//按你的需要添加多个任务:任务所在类.class   cron表达式  


return l.toArray(new CronTriggerImpl[l.size()]);  

    }  


private JobDetail create(Class c)  

    {  

JobDetailFactoryBean d =new JobDetailFactoryBean();  

d.setDurability(true);  

d.setRequestsRecovery(true);  

        d.setJobClass(c);  

        d.setName(c.getSimpleName() + Quartz_Job_Suffix);  

        d.setGroup(Quartz_Group_Name);  

        d.afterPropertiesSet();  

      JobDetail jd= d.getObject();  

//jd.getJobDataMap().put("key", 123);//如果想通过jobDataMap传递值,在这里添加  

return jd;  

}  

private CronTriggerImpl createTrigger(Class t, String cronExpression) throws ParseException {   

CronTriggerFactoryBean c =new CronTriggerFactoryBean();   

c.setJobDetail(create(t));   

c.setCronExpression(cronExpression);   

c.setName(t.getSimpleName() + Quartz_Trigger_Suffix);   

c.setGroup(Quartz_Group_Name); c.afterPropertiesSet();   

return (CronTriggerImpl)c.getObject();   

}   

@Bean(name = "schedulerFactoryBean")  

public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("triggers") CronTriggerImpl[] triggers)  

throws IOException, ParseException, SchedulerException  

    {  

SchedulerFactoryBean factory =new SchedulerFactoryBean();  

// 用于quartz集群,QuartzScheduler,启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了  

factory.setOverwriteExistingJobs(true);  

// QuartzScheduler 延时启动,应用启动完10秒后 QuartzScheduler 再启动  

factory.setStartupDelay(10);  

// 直接使用配置文件,用于quartz集群,加载quartz数据源配置  

factory.setConfigLocation(new ClassPathResource(QUARTZ_CONFIG));  

factory.setAutoStartup(true);  

// 集群需要通过QuartzJobBean注入,需要设置上下文  

factory.setApplicationContextSchedulerContextKey("applicationContext");  

// 注册触发器  

// factory.getScheduler().pauseAll();  

factory.setTriggers(createTriggers());// 直接使用配置文件  

// factory.setConfigLocation(new  

// FileSystemResource(this.getClass().getResource("/quartz.properties").getPath()));  

return factory;  

    }  


/**

     * 添加该方法的目的在于一个使用场景。如果代码中删除了不需要的定时任务,但是数据库中不会删除掉,会导致之前

     * 的定时任务一直在运行,如果把定时任务依赖的类删除了,就会导致报错,找不到目标。所以配置动态删除任务

     */  

@Bean  

public String fulsh(@Qualifier("schedulerFactoryBean") SchedulerFactoryBean schedulerFactoryBean,  

@Qualifier("triggers") CronTriggerImpl[] triggers)  

throws SchedulerException  

    {  

try  

        {  

            Scheduler s = schedulerFactoryBean.getScheduler();  

if (null == s)  

            {  

return "Scheduler is null";  

            }  


// 最新配置的任务  

List newTriNames =new ArrayList();  

if (null != triggers)  

            {  

for (CronTriggerImpl cronTriggerImpl : triggers)  

                {  

                    newTriNames.add(cronTriggerImpl.getName());  

                }  

            }  


// 现有数据库中已有的任务  

            Set myGroupTriggers = s.getTriggerKeys(GroupMatcher.triggerGroupEquals(Quartz_Group_Name));  

if (null == myGroupTriggers || myGroupTriggers.size() == 0)  

            {  

return "myGroupTriggers is null";  

            }  


if (newTriNames != null && newTriNames.size() > 0)  

            {  

for (TriggerKey triggerKey : myGroupTriggers)  

                {  

                    String dbTriggerName = triggerKey.getName();  

if (!newTriNames.contains(dbTriggerName))  

                    {  

// 暂停 触发器  

                        s.pauseTrigger(triggerKey);  

                        Trigger g = s.getTrigger(triggerKey);  

JobKey jk =null;  

if (null != g)  

                        {  

                            jk = g.getJobKey();  

                        }  

// 停止触发器  

                        s.pauseTrigger(triggerKey);  

// 注销 触发器  

                        s.unscheduleJob(triggerKey);  

if (null != jk)  

                        {  

// 暂停任务  

                            s.pauseJob(jk);  

// 删除任务  

                            s.deleteJob(jk);  

                        }  

                    }  

                }  

            }  

// 重要,如果不恢复所有,会导致无法使用  

            s.resumeAll();  

        }  

catch (Exception e)  

        {  

            e.printStackTrace();  

return "Exception:" + e.getMessage();  

        }  

return "success";  

    }  

}  

任务类:

[java] view plain copy

/*

 * 文件名:JobRefundWeichartBean.java 版权:Copyright by www.poly.com 描述: 修改人:gogym 修改时间:2017年12月28日 跟踪单号:

 * 修改单号: 修改内容:

 */  

package com.poly.pay.schedule;  


import org.quartz.DisallowConcurrentExecution;  

import org.quartz.JobExecutionContext;  

import org.quartz.JobExecutionException;  

import org.quartz.PersistJobDataAfterExecution;  


import org.springframework.scheduling.quartz.QuartzJobBean;  



// 1.修改数据,防止并发,2不允许并发执行  

@PersistJobDataAfterExecution  

@DisallowConcurrentExecution  

//@Component  

public class JobRefundWeichartBean extends QuartzJobBean {  

//需要注入的类,如redis代理  

private IRedisProxy redisProxy;  


@Override  

protected void executeInternal(JobExecutionContext arg0)  

throws JobExecutionException {  

//System.out.println("打印通过jobDataMap传递的值:"+arg0.getJobDetail().getJobDataMap().get("qqq"));  

//需要使用的时候,通过SpringContextUtil获取spring托管的实例注入即可  

redisProxy = SpringContextUtil.getBean(IRedisProxy.class);  

System.out.println("执行方法");  

    }  

}  

注意:在任务类里面注入,不能通过@Autowired注解直接注入。因为任务类本身并没有被spring托管,所以注入是null。可以这样解决

1、加入@Component类注解,让spring托管。但这种方法某些环境下不一定能使用,不推荐

2、通过自定义SpringContextUtil类来注入问题:

SpringContextUtil工具类:(注意,这个类要@Component注册到spring(前提是这个类要能被spring 扫描到)。也可以通过代码注册,在@Configuration类里面加入@Bean即可,这样可以手动注册。二者选其一就行)

[plain] view plain copy

import org.springframework.beans.BeansException;  

import org.springframework.beans.factory.NoSuchBeanDefinitionException;  

import org.springframework.context.ApplicationContext;  

import org.springframework.context.ApplicationContextAware;  

import org.springframework.stereotype.Component;  



/**  

 * 这个类是为了解决在普通类调用service的问题  

 *   

 * @ClassName SpringContextUtil  

 * @Description  

 * @author gogym 189155278@qq.com  

 * @date 2016-6-12  

 * @content OfflineMessageService offlineMessageService = (OfflineMessageService) SpringContextUtil  

 *          .getBean("offlineMessageService");  

 */  

@Component  

public class SpringContextUtil implements ApplicationContextAware  

{  

    private static ApplicationContext applicationContext; // Spring应用上下文  


    // 下面的这个方法上加了@Override注解,原因是继承ApplicationContextAware接口是必须实现的方法  

    @Override  

    public void setApplicationContext(ApplicationContext applicationContext)  

        throws BeansException  

    {  

        SpringContextUtil.applicationContext = applicationContext;  

    }  


    public static ApplicationContext getApplicationContext()  

    {  

        return applicationContext;  

    }  


    public static Object getBean(String name)  

        throws BeansException  

    {  

        return applicationContext.getBean(name);  

    }  


    public static Object getBean(String name, Class requiredType)  

        throws BeansException  

    {  


        return applicationContext.getBean(name, requiredType);  

    }  


    public static  T getBean(Class clazz)  

        throws BeansException  

    {  

        return applicationContext.getBean(clazz);  

    }  


    public static boolean containsBean(String name)  

    {  

        return applicationContext.containsBean(name);  

    }  


    public static boolean isSingleton(String name)  

        throws NoSuchBeanDefinitionException  

    {  

        return applicationContext.isSingleton(name);  

    }  


    public static Class getType(String name)  

        throws NoSuchBeanDefinitionException  

    {  

        return applicationContext.getType(name);  

    }  


    public static String[] getAliases(String name)  

        throws NoSuchBeanDefinitionException  

    {  

        return applicationContext.getAliases(name);  

    }  

}  

启动即可看到数据库插入了任务相关的信息。集群完成。

需要注意的是:

当你运行水平集群时,时钟应当要同步,以免出现离奇且不可预知的行为。假如时钟没能够同步,Scheduler 实例将对其他节点的状态产生混乱。有几种简单的方法来保证时钟何持同步,而且也没有理由不这么做。最简单的同步计算机时钟的方式是使用某一个 Internet 时间服务器(Internet Time Server ITS)。

常用解决方案:

服务器中配置时间同步只要一台服务器同步互联网的时钟服务器,其它的服务以这台为时钟服务器!

Linux配置(局域网的客户端)


1、安装

   yum install ntp   (centos的安装方法)

2、先运行 # ntpdate 192.168.1.33 同步一次.

3、然后通过crontab计时器配置一个定时同步的任务,例如每月一号零点零分同步一次.代码如下:

# crontab -e  //添下面一行,新建的定时任务文件保存在/var/spool/cron/下,以创建人的用户名命名

0 0 1 * * /etc/ntp/ntprsync.sh//每小时同步一次。


4、创建文件


# vi ntprsync.sh//内容如下#!/bin/sh/usr/sbin/ntpdate 192.168.1.33//时钟服务器的IP/sbin/hwclock –w


5、设置权限 chmod 777 ntprsync.sh

6、注意防火墙的设置.

7、成功。

8、服务启动。


/sbin/service crond start //启动服务/sbin/service crond stop //关闭服务/sbin/service crond restart //重启服务/sbin/service crond reload //重新载入配置 



转载自https://blog.csdn.net/KokJuis/article/details/78526709

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容