Spring整合Quartz分布式调度
前言
为了保证应用的高可用和高并发性,一般都会部署多个节点;对于定时任务,如果每个节点都执行自己的定时任务,一方面耗费了系统资源,另一方面有些任务多次执行,可能引发应用逻辑问题,所以需要一个分布式的调度系统,来协调每个节点执行定时任务。
Spring整合Quartz
Quartz是一个成熟的任务调度系统,Spring对Quartz做了兼容,方便开发,下面看看具体如何整合:
1.Maven依赖文件
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
</dependencies>
主要就是Spring相关库、quartz库以及mysql驱动库,注:分布式调度需要用到数据库,这里选用mysql;
2.配置job
提供了两种方式来配置job,分别是:MethodInvokingJobDetailFactoryBean和JobDetailFactoryBean
2.1MethodInvokingJobDetailFactoryBean
要调用特定bean的一个方法的时候使用,具体配置如下:
<bean id="firstTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="firstService" />
<property name="targetMethod" value="service" />
</bea>
2.2JobDetailFactoryBean
这种方式更加灵活,可以设置传递参数,具体如下:
<bean id="firstTask"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="zh.maven.SQuartz.task.FirstTask" />
<property name="jobDataMap">
<map>
<entry key="firstService" value-ref="firstService" />
</map>
</property>
</bean>
jobClass定义的任务类,继承QuartzJobBean,实现executeInternal方法;jobDataMap用来给job传递数据;
3.配置调度使用的触发器
同样提供了两种触发器类型:SimpleTriggerFactoryBean和CronTriggerFactoryBean
重点看CronTriggerFactoryBean,这种类型更加灵活,具体如下:
<bean id="firstCronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="firstTask" />
<property name="cronExpression" value="0/5 * * ? * *" />
</bean>
jobDetail指定的就是在步骤2中配置的job,cronExpression配置了每5秒执行一次job;
4.配置Quartz调度器的SchedulerFactoryBean
同样提供了两种方式:内存RAMJobStore和数据库方式
4.1内存RAMJobStore
job的相关信息存储在内存里,每个节点存储各自的,互相隔离,配置如下:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="firstCronTrigger" />
</list>
</property>
</bean>
4.2数据库方式
job的相关信息存储在数据库中,所有节点共用数据库,每个节点通过数据库来通信,保证一个job同一时间只会在一个节点上执行,并且
如果某个节点挂掉,job会被分配到其他节点执行,具体配置如下:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/quartz" />
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:quartz.properties" />
<property name="triggers">
<list>
<ref bean="firstCronTrigger" />
</list>
</property>
</bean>
dataSource用来配置数据源,数据表相关信息,可以到官网下载gz包,sql文件在路径:docsdbTables下,里面提供了主流数据库的sql文件,总共11张表;
configLocation配置的quartz.properties文件在quartz.jar的org.quartz包下,里面提供了一些默认的数据,比如org.quartz.jobStore.class
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
这里需要将quartz.properties拷贝出来做一些修改,具体修改如下:
org.quartz.scheduler.instanceId: AUTO
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered: true
org.quartz.jobStore.clusterCheckinInterval: 1000
5.相关类
public class FirstTask extends QuartzJobBean {
private FirstService firstService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
firstService.service();
}
public void setFirstService(FirstService firstService) {
this.firstService = firstService;
}
}
FirstTask继承QuartzJobBean,实现executeInternal方法,调用FirstService;
public class FirstService implements Serializable {
private static final long serialVersionUID = 1L;
public void service() {
System.out.println(new SimpleDateFormat("YYYYMMdd HH:mm:ss").format(new Date()) + "---start FirstService");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(new SimpleDateFormat("YYYYMMdd HH:mm:ss").format(new Date()) + "---end FirstService");
}
}
FirstService需要提供序列化接口,因为需要保存在数据库中;
public class App {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartz.xml");
}
}
主类用来加载quartz配置文件;
测试分布式调度
1.同时启动App两次,观察日志:
20180405 14:48:10---start FirstService
20180405 14:48:12---end FirstService
20180405 14:48:15---start FirstService
20180405 14:48:17---end FirstService
其中A1有日志输出,A2没有;当停掉A1以后,A2有日志输出;
2.添加新的job分别新建:SecondTask和SecondService,同时添加相关配置文件,启动App观察日志:
A1日志如下:
20180405 15:03:15---start FirstService
20180405 15:03:15---start SecondService
20180405 15:03:17---end FirstService
20180405 15:03:17---end SecondService
20180405 15:03:20---start FirstService
20180405 15:03:22---end FirstService
20180405 15:03:25---start FirstService
20180405 15:03:27---end FirstService
A2日志如下:
20180405 15:03:20---start SecondService
20180405 15:03:22---end SecondService
20180405 15:03:25---start SecondService
20180405 15:03:27---end SecondService
可以发现A1和A2都有执行任务,但是同一任务同一时间只会在一个节点执行,并且只有在执行结束后才有可能分配到其他节点;
3.如果间隔时间小于任务执行时间,比如这里改成sleep(6000)
A1日志如下:
20180405 15:14:40---start FirstService
20180405 15:14:45---start FirstService
20180405 15:14:46---end FirstService
20180405 15:14:50---start FirstService
20180405 15:14:50---start SecondService
20180405 15:14:51---end FirstService
A2日志如下:
20180405 15:14:40---start SecondService
20180405 15:14:45---start SecondService
20180405 15:14:46---end SecondService
20180405 15:14:51---end SecondService
间隔时间是5秒,而任务执行需要6秒,观察日志可以发现,任务还没有结束,新的任务已经开始,这种情况可能引发应用的逻辑问题,其实就是任务能不能支持串行的问题;
4.@DisallowConcurrentExecution注解保证任务的串行
在FirstTask和SecondTask上分别添加@DisallowConcurrentExecution注解,日志结果如下:
A1日志如下:
20180405 15:32:45---start FirstService
20180405 15:32:51---end FirstService
20180405 15:32:51---start FirstService
20180405 15:32:51---start SecondService
20180405 15:32:57---end FirstService
20180405 15:32:57---end SecondService
20180405 15:32:57---start FirstService
20180405 15:32:57---start SecondService
A2日志如下:
20180405 15:32:45---start SecondService
20180405 15:32:51---end SecondService
观察日志可以发现,任务只有在end以后,才会开始新的任务,实现了任务的串行化;
总结
本文旨在对Spring+Quartz分布式调度有一个直观的了解,通过实际的使用来解决问题,当然可能还有很多疑问比如它是如何调度的,数据库如果挂了会怎么样等等,还需要做更加深入的了解。
Spring整合Quartz分布式调度的更多相关文章
- Spring整合Quartz分布式调度(山东数漫江湖)
前言 为了保证应用的高可用和高并发性,一般都会部署多个节点:对于定时任务,如果每个节点都执行自己的定时任务,一方面耗费了系统资源,另一方面有些任务多次执行,可能引发应用逻辑问题,所以需要一个分布式的调 ...
- Spring整合Quartz分布式定时任务
概述虽然单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性.高可靠性满足.假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部 ...
- 使用Spring整合Quartz轻松完成定时任务
一.背景 上次我们介绍了如何使用Spring Task进行完成定时任务的编写,这次我们使用Spring整合Quartz的方式来再一次实现定时任务的开发,以下奉上开发步骤及注意事项等. 二.开发环境及必 ...
- Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)
Spring整合Quartz定时任务 在集群.分布式系统中的应用(Mysql数据库环境) 转载:http://www.cnblogs.com/jiafuwei/p/6145280.html 单个Q ...
- SpringBoot整合Quartz作为调度中心完整实用例子
因为想要做一个类似于调度中心的东西,定时执行一些Job(通常是一些自定义程序或者可执行的jar包),搭了一个例子,总结了前辈们的相关经验和自己的一些理解,如有雷同或不当之处,望各位大佬见谅和帮忙指正. ...
- 初识quartz 并分析 项目中spring整合quartz的配置【原创+转载】
初识quartz 并分析 项目中spring整合quartz的配置[原创+转载]2018年01月29日 12:08:07 守望dfdfdf 阅读数:114 标签: quartz 更多个人分类: 工具 ...
- Spring整合Quartz轻松完成定时任务
一.背景 上次我们介绍了如何使用Spring Task进行完成定时任务的编写,这次我们使用Spring整合Quartz的方式来再一次实现定时任务的开发,以下奉上开发步骤及注意事项等. 二.开发环境及必 ...
- spring整合quartz并持久化
spring整合quartz有两种方式: 一.常见是使用配置文件,将定时任务保存到内存中 简单示例: <!-- 短信催还提醒任务调度 --> <bean id="overd ...
- Spring整合Quartz定时任务执行2次,Spring定时任务执行2次
Spring整合Quartz定时任务执行2次,Spring定时任务执行2次 >>>>>>>>>>>>>>>&g ...
随机推荐
- RabbitMQ 安装,配置
1:安装 yum install -y rabbitmq-server 2:主要程序介绍 # 管理插件的程序 /usr/sbin/rabbitmq-plugins # 服务程序 /usr/sbin ...
- Java实现的词频统计——功能改进
本次改进是在原有功能需求及代码基础上额外做的修改,保证了原有的基础需求之外添加了新需求的功能. 功能: 1. 小文件输入——从控制台由用户输入到文件中,再对文件进行统计: 2.支持命令行输入英文作品的 ...
- PAT L1-034 点赞
https://pintia.cn/problem-sets/994805046380707840/problems/994805098188750848 微博上有个“点赞”功能,你可以为你喜欢的博文 ...
- static关键字的新用法
static关键字的新用法和总结: static这个关键字,也可以像“self”一样,代表“当前类”,用于访问一个类的“静态属性或静态方法”: 但, static,在应用中,更灵活,因此更常见! 因为 ...
- RabbitMQ 使用详细介绍
1. 实现最简单的队列通信 2. producer端 # !/usr/bin/env python import pika #通过这个实例,先去建立一个socket,默认端口15672 connect ...
- 【转】.gitignore失效的解决办法
转自:http://foreverdo.diandian.com/post/2012-09-20/40038034798 How to make .gitignore works? Just got ...
- bzoj3748 Kwadraty
Claris 当然是要用来%的 但是,,其他dalao,,比如JL的红太阳commonc.题解能不能稍微加几句话,蒟蒻看不懂啊. 在这里解释一下,Claris的题解.(因为我弱,想了半天才明白,所以觉 ...
- ros error : c++: error: $(catkin_LIBRARIES): 没有那个文件或目录
卧槽,真是........................瞎眼了. 一个半小时才找出错误来..... c++: error: $(catkin_LIBRARIES): 没有那个文件或目录 Oh my ...
- UESTC--1732
原题链接:http://acm.uestc.edu.cn/problem.php?pid=1732 分析:dp,n个相同物品放入m个相同的盒子(允许为空)的个数为dp[n][m]=dp[n][m-1] ...
- 洛谷P2398 GCD SUM (数学)
洛谷P2398 GCD SUM 题目描述 for i=1 to n for j=1 to n sum+=gcd(i,j) 给出n求sum. gcd(x,y)表示x,y的最大公约数. 输入输出格式 输入 ...