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集群势必成为你应用的一部 ...
- SSM三大框架整合详细总结(Spring+SpringMVC+MyBatis)(山东数漫江湖)
使用 SSM ( Spring . SpringMVC 和 Mybatis )已经很久了,项目在技术上已经没有什么难点了,基于现有的技术就可以实现想要的功能,当然肯定有很多可以改进的地方.之前没有记录 ...
- Spring boot 集成Dubbox(山东数漫江湖)
前言 因为工作原因,需要在项目中集成dubbo,所以去查询dubbo相关文档,发现dubbo目前已经不更新了,所以把目光投向了dubbox,dubbox是当当网基于dubbo二次开发的一个项目,dub ...
- Spring boot集成RabbitMQ(山东数漫江湖)
RabbitMQ简介 RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统 MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出 ...
- Spring Session加Redis(山东数漫江湖)
session是一个非常常见的概念.session的作用是为了辅助http协议,因为http是本身是一个无状态协议.为了记录用户的状态,session机制就应运而生了.同时session也是一个非常老 ...
- Spring mvc详解(山东数漫江湖)
Spring mvc框架 Spring web MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活.松散耦合的 web 应用程序的组件.MVC 模式导致了应用程序的不同方面(输入逻辑.业务 ...
- 使用Spring整合Quartz轻松完成定时任务
一.背景 上次我们介绍了如何使用Spring Task进行完成定时任务的编写,这次我们使用Spring整合Quartz的方式来再一次实现定时任务的开发,以下奉上开发步骤及注意事项等. 二.开发环境及必 ...
- Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)
Spring整合Quartz定时任务 在集群.分布式系统中的应用(Mysql数据库环境) 转载:http://www.cnblogs.com/jiafuwei/p/6145280.html 单个Q ...
随机推荐
- 【Linux】- CentOS 7 安装.NET Core 2.1
添加dotnet产品Feed 在安装.NET Core之前,您需要注册Microsoft产品Feed. 这只需要做一次. 首先,注册Microsoft签名密钥,然后添加Microsoft产品Feed. ...
- 【bzoj2190】[SDOI2008]仪仗队 欧拉函数
题目描述 作为体育委员,C君负责这次运动会仪仗队的训练.仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图 ...
- JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)
什么是面向对象?面向对象是一种思想. 面向对象可以把程序中的关键模块都视为对象, 而模块拥有属性及方法. 这样如果我们把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作. 工厂 ...
- 前台界面(1)---HTML基本定义及常见标签
已经很久没有更新博客了,从今天开始要继续走在学习的路上,话不多说,先来干货: 目录 1. HTML定义 2. H标签 3. Img标签 4. P标签 5. A标签 6. 无序列表 7. 有序列表 8. ...
- [APIO2017]商旅 0/1分数规划
---题面--- 题解: upd: 在洛谷上被Hack了...思路应该是对的,代码就别看了 感觉有个地方还是非常妙的,就是因为在x买东西,在y卖出,就相当于直接从x走向了y,因为经过中间的城市反正也不 ...
- 项目管理---git----快速使用git笔记(三)------coding.net注册和新建项目(远程仓库)
我们在第一章已经了解了github和coding.net的区别: github是一个基于git的代码托管平台,付费用户可以建私人仓库,我们一般的免费用户只能使用公共仓库,也就是代码要公开. codin ...
- bzoj 1150&2151&2288(双向链表+堆)(贪心)
经典模型:在n个点中选k个点,要求两两不相邻,且总权值最大/最小. 做法:用双向链表串起来,把所有点丢进堆里,选择一个点的时候把它左右两个点从双向链表和堆中去除,然后把这个点的权值加进ans,出堆后改 ...
- bzoj2330: [SCOI2011]糖果(差分约束)
差分约束裸题,a==b的话分别建a>=b a<=b的边就行.倒序加边不然会TLE是什么鬼 #include<iostream> #include<cstring> ...
- 探索CAS无锁技术
前言:关于同步,很多人都知道synchronized,Reentrantlock等加锁技术,这种方式也很好理解,是在线程访问的临界区资源上建立一个阻塞机制,需要线程等待 其它线程释放了锁,它才能运行. ...
- poco普通线程
#include "Poco/Thread.h" #include "Poco/RunnableAdapter.h" #include <iostream ...