Spring整合Quartz分布式調(diào)度的示例代碼
前言
為了保證應(yīng)用的高可用和高并發(fā)性,一般都會(huì)部署多個(gè)節(jié)點(diǎn);對(duì)于定時(shí)任務(wù),如果每個(gè)節(jié)點(diǎn)都執(zhí)行自己的定時(shí)任務(wù),一方面耗費(fèi)了系統(tǒng)資源,
另一方面有些任務(wù)多次執(zhí)行,可能引發(fā)應(yīng)用邏輯問(wèn)題,所以需要一個(gè)分布式的調(diào)度系統(tǒng),來(lái)協(xié)調(diào)每個(gè)節(jié)點(diǎn)執(zhí)行定時(shí)任務(wù)。
Spring整合Quartz
Quartz是一個(gè)成熟的任務(wù)調(diào)度系統(tǒng),Spring對(duì)Quartz做了兼容,方便開(kāi)發(fā),下面看看具體如何整合:
1.Maven依賴(lài)文件
<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相關(guān)庫(kù)、quartz庫(kù)以及mysql驅(qū)動(dòng)庫(kù),注:分布式調(diào)度需要用到數(shù)據(jù)庫(kù),這里選用mysql;
2.配置job
提供了兩種方式來(lái)配置job,分別是:MethodInvokingJobDetailFactoryBean和JobDetailFactoryBean
2.1MethodInvokingJobDetailFactoryBean
要調(diào)用特定bean的一個(gè)方法的時(shí)候使用,具體配置如下:
<bean id="firstTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="firstService" /> <property name="targetMethod" value="service" /> </bea>
2.2JobDetailFactoryBean
這種方式更加靈活,可以設(shè)置傳遞參數(shù),具體如下:
<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定義的任務(wù)類(lèi),繼承QuartzJobBean,實(shí)現(xiàn)executeInternal方法;jobDataMap用來(lái)給job傳遞數(shù)據(jù)
3.配置調(diào)度使用的觸發(fā)器
同樣提供了兩種觸發(fā)器類(lèi)型:SimpleTriggerFactoryBean和CronTriggerFactoryBean
重點(diǎn)看CronTriggerFactoryBean,這種類(lèi)型更加靈活,具體如下:
<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秒執(zhí)行一次job;
4.配置Quartz調(diào)度器的SchedulerFactoryBean
同樣提供了兩種方式:內(nèi)存RAMJobStore和數(shù)據(jù)庫(kù)方式
4.1內(nèi)存RAMJobStore
job的相關(guān)信息存儲(chǔ)在內(nèi)存里,每個(gè)節(jié)點(diǎn)存儲(chǔ)各自的,互相隔離,配置如下:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="firstCronTrigger" /> </list> </property> </bean>
4.2數(shù)據(jù)庫(kù)方式
job的相關(guān)信息存儲(chǔ)在數(shù)據(jù)庫(kù)中,所有節(jié)點(diǎn)共用數(shù)據(jù)庫(kù),每個(gè)節(jié)點(diǎn)通過(guò)數(shù)據(jù)庫(kù)來(lái)通信,保證一個(gè)job同一時(shí)間只會(huì)在一個(gè)節(jié)點(diǎn)上執(zhí)行,并且
如果某個(gè)節(jié)點(diǎn)掛掉,job會(huì)被分配到其他節(jié)點(diǎn)執(zhí)行,具體配置如下:
<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用來(lái)配置數(shù)據(jù)源,數(shù)據(jù)表相關(guān)信息,可以到官網(wǎng)下載gz包,sql文件在路徑:docs\dbTables下,里面提供了主流數(shù)據(jù)庫(kù)的sql文件;
configLocation配置的quartz.properties文件在quartz.jar的org.quartz包下,里面提供了一些默認(rèn)的數(shù)據(jù),比如org.quartz.jobStore.class
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
這里需要將quartz.properties拷貝出來(lái)做一些修改,具體修改如下:
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.相關(guān)類(lèi)
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,實(shí)現(xiàn)executeInternal方法,調(diào)用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需要提供序列化接口,因?yàn)樾枰4嬖跀?shù)據(jù)庫(kù)中;
public class App { public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartz.xml"); } }
主類(lèi)用來(lái)加載quartz配置文件;
測(cè)試分布式調(diào)度
1.同時(shí)啟動(dòng)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沒(méi)有;當(dāng)停掉A1以后,A2有日志輸出;
2.添加新的job分別新建:SecondTask和SecondService,同時(shí)添加相關(guān)配置文件,啟動(dòng)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
可以發(fā)現(xiàn)A1和A2都有執(zhí)行任務(wù),但是同一任務(wù)同一時(shí)間只會(huì)在一個(gè)節(jié)點(diǎn)執(zhí)行,并且只有在執(zhí)行結(jié)束后才有可能分配到其他節(jié)點(diǎn);
3.如果間隔時(shí)間小于任務(wù)執(zhí)行時(shí)間,比如這里改成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
間隔時(shí)間是5秒,而任務(wù)執(zhí)行需要6秒,觀察日志可以發(fā)現(xiàn),任務(wù)還沒(méi)有結(jié)束,新的任務(wù)已經(jīng)開(kāi)始,這種情況可能引發(fā)應(yīng)用的邏輯問(wèn)題,其實(shí)就是任務(wù)能不能支持串行的問(wèn)題;
4.@DisallowConcurrentExecution注解保證任務(wù)的串行
在FirstTask和SecondTask上分別添加@DisallowConcurrentExecution注解,日志結(jié)果如下:
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
觀察日志可以發(fā)現(xiàn),任務(wù)只有在end以后,才會(huì)開(kāi)始新的任務(wù),實(shí)現(xiàn)了任務(wù)的串行化;
總結(jié)
本文旨在對(duì)Spring+Quartz分布式調(diào)度有一個(gè)直觀的了解,通過(guò)實(shí)際的使用來(lái)解決問(wèn)題,當(dāng)然可能還有很多疑問(wèn)比如它是如何調(diào)度的,數(shù)據(jù)庫(kù)如果掛了會(huì)怎么樣等等,還需要做更加深入的了解。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JAVA使用commos-fileupload實(shí)現(xiàn)文件上傳與下載實(shí)例解析
這篇文章主要介紹了JAVA使用commos-fileupload實(shí)現(xiàn)文件上傳與下載的相關(guān)資料,需要的朋友可以參考下2016-02-02SpringBoot中@Scheduled實(shí)現(xiàn)服務(wù)啟動(dòng)時(shí)執(zhí)行一次
本文主要介紹了SpringBoot中@Scheduled實(shí)現(xiàn)服務(wù)啟動(dòng)時(shí)執(zhí)行一次,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08SpringBoot監(jiān)聽(tīng)?wèi)?yīng)用程序啟動(dòng)的生命周期事件的四種方法
在 Spring Boot 中,監(jiān)聽(tīng)?wèi)?yīng)用程序啟動(dòng)的生命周期事件有多種方法,本文給大家就介紹了四種監(jiān)聽(tīng)?wèi)?yīng)用程序啟動(dòng)的生命周期事件的方法,并通過(guò)代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-07-07分析Java中ArrayList與LinkedList列表結(jié)構(gòu)的源碼
這篇文章主要介紹了Java中ArrayList與LinkedList列表結(jié)構(gòu)的源碼,文章最后對(duì)LinkedList和ArrayList以及Vector的特性有一個(gè)對(duì)比總結(jié),需要的朋友可以參考下2016-05-05Java簡(jiǎn)單數(shù)據(jù)加密方法DES實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Java簡(jiǎn)單數(shù)據(jù)加密方法DES實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12SpringBoot集成MyBatis對(duì)管理員的查詢(xún)操作
本文主要介紹了SpringBoot集成MyBatis對(duì)管理員的查詢(xún)操作,實(shí)現(xiàn)增刪改查中的查詢(xún)操作,對(duì)所有的普通管理員進(jìn)行查詢(xún)操作,感興趣的可以了解一下2023-11-11Spring?BOOT?AOP基礎(chǔ)應(yīng)用教程
這篇文章主要介紹了Spring?BOOT?AOP的使用,文章從相關(guān)問(wèn)題展開(kāi)全文內(nèi)容詳情,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07