技术好文:quartz基本介绍和使用

简介: 技术好文:quartz基本介绍和使用

一、什么是quartz,有什么用。


  Quartz是一个完全由java编写的开源作业调度框架,由OpenSymphony组织开源出来。所谓作业调度其实就是按照程序的设定,某一时刻或者时间间隔去执行某个代码。最常用的就是报表的制作了。


二、quartz的简单事例


public class HelloJob implements Job {


@Override


public void execute(JobExecutionContext context) throws JobExecutionException {


Object tv1 = context.getTrigger().getJobDataMap().get("t1");


Object tv2 = context.getTrigger().getJobDataMap().get("t2");


Object jv1 = context.getJobDetail().getJobDataMap().get("j1");


Object jv2 = context.getJobDetail().getJobDataMap().get("j2");


Object sv = null;


try {


sv = context.getScheduler().getContext().get("skey");


} catch (SchedulerException e) {


e.printStackTrace();


}


System.out.println(tv1+":"+tv2);


System.out.println(jv1+":"+jv2);


System.out.println(sv);


System.out.println("hello:"+LocalDateTime.now());


}


}


public class Test {


public static void main(String【】 args) throws SchedulerException {


//创建一个scheduler


Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();


scheduler.getContext().put("skey", "svalue");


//创建一个Trigger


Trigger trigger = TriggerBuilder.newTrigger()


.withIdentity("trigger1", "group1")


.usingJobData("t1", "//代码效果参考:http://hnjlyzjd.com/xl/wz_25138.html

tv1")

.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3)


.repeatForever()).build();


trigger.getJobDataMap().put("t2", "tv2");


//创建一个job


JobDetail job = JobBuilder.newJob(HelloJob.class)


.usingJobData("j1", "jv1")


.withIdentity("myjob", "mygroup").build();


job.getJobDataMap().put("j2", "jv2");


//注册trigger并启动scheduler


scheduler.scheduleJob(job,trigger);


scheduler.start();


//代码效果参考:http://hnjlyzjd.com/xl/wz_25136.html

}

}


二、quartz的基本组成


  1、调度器--------------Scheduler


    Scheduler被用来对Trigger和Job进行管理。Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中都拥有自己的唯一的组和名称用来进行彼此的区分,Scheduler可以通过组名或者名称来对Trigger和JobDetail来进行管理。一个Trigger只能对应一个Job,但是一个Job可以对应多个Trigger。每个Scheduler都包含一个SchedulerContext,用来保存Scheduler的上下文。Job和Trigger都可以获取SchedulerContext中的信息。


    Scheduler包含两个重要的组件,JobStore和ThreadPool。JobStore用来存储运行时信息,包括Trigger,Schduler,JobDetail,业务锁等。它有多种实现RAMJob(内存实现),JobStoreTX(JDBC,事务由Quartz管理)等。ThreadPool就是线程池,Quartz有自己的线程池实现。所有任务的都会由线程池执行。


  Scheduler是由SchdulerFactory创建,它有两个实现:DirectSchedulerFactory和 StdSchdulerFactory。前者可以用来在代码里定制你自己的Schduler参数。后者是直接读取classpath下的quartz.properties(不存在就都使用默认值)配置来实例化Schduler。通常来讲,我们使用StdSchdulerFactory也就足够了


  2、触发器--------------Trigger


    Trigger是用来定义Job的执行规则,主要有四种触发器,其中SimpleTrigger和CronTrigger触发器用的最多。


      SimpleTrigger:从某一个时间开始,以一定的时间间隔来执行任务。它主要有两个属性,repeatInterval 重复的时间间隔;repeatCount 重复的次数,实际上执行的次数是n+1,因为在startTime的时候会执行一次。


      CronTrigger:适合于复杂的任务,使用cron表达式来定义执行规则。


      CalendarIntervalTrigger:类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。 但是CalendarIntervalTrigger执行任务的时间间隔比SimpleTrigger要丰富,它支持的间隔单位有秒,分钟,小时,天,月,年,星期。相较于SimpleTrigger有两个优势:1、更方便,比如每隔1小时执行,你不用自己去计算1小时等于多少毫秒。 2、支持不是固定长度的间隔,比如间隔为月和年。但劣势是精度只能到秒。它的主要两个属性,interval 执行间隔;intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)


      DailyTimeIntervalTrigger:指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。它适合的任务类似于:指定每天9:00 至 18:00 ,每隔70秒执行一次,并且只要周一至周五执行。它的属性有startTimeOfDay 每天开始时间;endTimeOfDay 每天结束时间;daysOfWeek 需要执行的星期;interval 执行间隔;intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期);repeatCount 重复次数


    所有的trigger都包含了StartTime和endTIme这两个属性,用来指定Trigger被触发的时间区间。


    所有的trigger都可以设置MisFire策略,该策略是对于由于系统奔溃或者任务时间过长等原因导致trigger在应该触发的时间点没有触发,并且超过了misfireThreshold设置的时间(默认是一分钟,没有超过就立即执行)就算misfire了,这个时候就该设置如何应对这种变化了。激活失败指令(Misfire Instructions)是触发器的一个重要属性,它指定了misfire发生时调度器应当如何处理。所有类型的触发器都有一个默认的指令,叫做Trigger.MISFIRE_INSTRUCTION_SMART_POLICY,但是这个这个“聪明策略”对于不同类型的触发器其具体行为是不同的。对于SimpleTrigger,这个“聪明策略”将根据触发器实例的状态和配置来决定其行 为。具体如下【】:


      如果Repeat Count=0:只执行一次


        instruction selected = MISFIRE_INSTRUCTION_FIRE_NOW;


      如果Repeat Count=REPEAT_INDEFINITELY:无限次执行


        instruction selected = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;


      如果Repeat Count>0:  执行多次(有限)


        instruction selected = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT;


      下面解释SimpleTrigger常见策略:


        MISFIRE_INSTRUCTION_FIRE_NOW   立刻执行。对于不会重复执行的任务,这是默认的处理策略。


        MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT   在下一个激活点执行,且超时期内错过的执行机会作废。


        MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_COUNT   立即执行,且超时期内错过的执行机会作废。


        MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT    在下一个激活点执行,并重复到指定的次数。


        MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_COUNT     立即执行,并重复到指定的次数。


        MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY              忽略所有的超时状态,按照触发器的策略执行。


  对于CronTrigger,该“聪明策略”默认选择MISFIRE_INSTRUCTION_FIRE_ONCE_NOW以指导其行为。


      下面解释CronTrigger常见策略:


        MISFIRE_INSTRUCTION_FIRE_ONCE_NOW  立刻执行一次,然后就按照正常的计划执行。


        MISFIRE_INSTRUCTION_DO_NOTHING    目前不执行,然后就按照正常的计划执行。这意味着如果下次执行时间超过了end time,实际上就没有执行机会了。


  3、任务-----------------Job


    Job是一个任务接口,开发者定义自己的任务须实现该接口实现void execute(JobExecutionContext context)方法,JobExecutionContext中提供了调度上下文的各种信息。Job中的任务有可能并发执行,例如任务的执行时间过长,而每次触发的时间间隔太短,则会导致任务会被并发执行。如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。可以在execute()方法上添加注解@DisallowConcurrentExecution解决这个问题。


  4、任务详情-----------JobDetail


    Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。所以说JobDetail是任务的定义,而Job是任务的执行逻辑。


  5、日历------------------Calendar


    Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。


    主要有以下Calendar


     HolidayCalendar。指定特定的日期,比如20140613。精度到天。


     DailyCalendar。指定每天的时间段(rangeStartingTime, rangeEndingTime),格式是HH:MM【:SS【:mmm】】。也就是最大精度可以到毫秒。


     WeeklyCalendar。指定每星期的星期几,可选值比如为java.util.Calendar.SUNDAY。精度是天。


     MonthlyCalendar。指定每月的几号。可选值为1-31。精度是天


     AnnualCalendar。 指定每年的哪一天。使用方式如上例。精度是天。


     CronCalendar。指定Cron表达式。精度取决于Cron表达式,也就是最大精度可以到秒。


  6、JobDataMap


    用来保存JobDetail运行时的信息,JobDataMap的使用:.usingJobData("name","kyle")或者.getJobDataMap("name","kyle")


  7、cron表达式


    Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感


    星号():可用在所有字段中,表示对应时间域的每一个时刻,例如, 在分钟字段时,表示“每分钟”;


    问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;


    减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;


    逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;


    斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用/y,它等同于0/y;


    L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;


    W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;


    LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;


井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;


    C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。


  8、quartz.properties文件编写


####################RAMJob的配置##################


#在集群中每个实例都必须有一个唯一的instanceId,但是应该有一个相同的instanceName【默认“QuartzScheduler”】【非必须】


org.quartz.scheduler.instanceName = MyScheduler


#Scheduler实例ID,全局唯一,【默认值NON_CLUSTERED】,或者可以使用“SYS_PROP”通过系统属性设置id。【非必须】


org.quartz.scheduler.instanceId=AUTO


# 线程池的实现类(定长线程池,几乎可满足所有用户的需求)【默认null】【必须】


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


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


org.quartz.threadPool.threadCount = 25


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


org.quartz.threadPool.threadPriority = 5


#misfire设置的时间默认为一分钟


org.quartz.jobStore.misfireThreshold=60000


# 将schedule相关信息保存在RAM中,轻量级,速度快,遗憾的是应用重启时相关信息都将丢失。


org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore


# 建议设置为“org.terracotta.quartz.skipUpdateCheck=true”不会在程序运行中还去检查quartz是否有版本更新。【默认false】【非必须】


org.quartz.scheduler.skipUpdateCheck?=?true


###################jdbcJobStore############################


org.quartz.scheduler.instanceName=MyScheduler


org.quartz.scheduler.instanceId=AUTO


org.quartz.threadPool.threadCount=3


# 所有的quartz数据例如job和Trigger的细节信息被保存在内存或数据库中,有两种实现:JobStoreTX(自己管理事务)


#和JobStoreCMT(application server管理事务,即全局事务JTA)


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


# 类似于Hibernate的dialect,用于处理DB之间的差异,StdJDBCDelegate能满足大部分的DB


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


#数据库表的前缀


org.quartz.jobStore.tablePrefix=QRTZ


#配置数据源的名称


org.quartz.jobStore.dataSource=myDS


#为了指示JDBCJobStore所有的JobDataMaps中的值都是字符串,并且能以“名字-值”对的方式存储而不是以复杂对象的序列化形式存储在BLOB字段中,应该设置为true(缺省方式)


org.quartz.jobStore.useProperties = true


# 检入到数据库中的频率(毫秒)。检查是否其他的实例到了应当检入的时候未检入这能指出一个失败的实例,


#且当前Scheduler会以此来接管执行失败并可恢复的Job通过检入操作,Scheduler也会更新自身的状态记录


org.quartz.jobStore.clusterCheckinInterval=20000


# 是否集群、负载均衡、容错,如果应用在集群中设置为false会出错


org.quartz.jobStore.isClustered=false


#misfire时间设置


org.quartz.jobStore.misfireThreshold=60000


# 连接超时重试连接的间隔。使用 RamJobStore时,该参数并没什么用【默认15000】【非必须】


#org.quartz.scheduler.dbFailureRetryInterval = 15000


#下面是数据库链接相关的配置


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


org.quartz.dataSource.myDS.URL=jdbc:


org.quartz.dataSource.myDS.user=root


org.quartz.dataSource.myDS.password=root


org.quartz.dataSource.myDS.maxConnections=5


三、quartz的简单使用


  1、首先就是引入相关的jar包


  2、编写quartz.properies文件:参考上面的properties文件的编写。


  3、编写代码


@DisallowConcurrentExecution


public class MyJob implements Job {


@Override


public void execute(JobExecutionContext arg0) throws JobExecutionException {


String name = arg0.getJobDetail().getKey().getName();


System.out.println(name+"这是第一个定时任务"+LocalDateTime.now());


}


}


public class Test {


public static void main(String【】 args) throws SchedulerException, InterruptedException {


Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();


JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("MyJob","Group1").build();


JobDetail jobDetail2 = JobBuilder.newJob(MyJob.class).withIdentity("MyJob2","Group2").build();


JobDetail jobDetail3 = JobBuilder.newJob(MyJob.class).withIdentity("MyJob3","Group3").build();


JobDetail jobDetail4 = JobBuilder.newJob(MyJob.class).withIdentity("MyJob4","Group4").build();


Calendar cal1 = Calendar.getInstance();


cal1.set(Calendar.MINUTE, 28);


cal1.set(Calendar.SECOND, 0);


Calendar cal2 = Calendar.getInstance();


cal2.set(Calendar.MINUTE, 29);


cal2.set(Calendar.SECOND, 0);


DailyCalendar dailyCalendar = new DailyCalendar(cal1, cal2);


scheduler.addCalendar("dailyCalendar", dailyCalendar, false, false);


CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("tigger1","TGroup1")


.withSchedule(CronScheduleBuilder.cronSchedule("/2 * ?")).build();


SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger2","TGroup2")


.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(5, 10)).build();


CalendarIntervalTrigger citrigger = TriggerBuilder.newTrigger().withIdentity("trigger3","TGroup3")


.withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule()


.withIntervalInSeconds(10)).modifiedByCalendar("dailyCalendar").build();


DailyTimeIntervalTrigger dtiTrigger = TriggerBuilder.newTrigger().withIdentity("trigger4","TGroup4")


.withSchedule(DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()


.startingDailyAt(TimeOfDay.hourAndMinuteOfDay(16, 43))


.endingDailyAt(TimeOfDay.hourAndMinuteOfDay(16, 44))


.withIntervalInSeconds(5)


.withRepeatCount(20))


.build();


scheduler.scheduleJob(jobDetail, cronTrigger);


scheduler.scheduleJob(jobDetail2, simpleTrigger);


scheduler.scheduleJob(jobDetail3, citrigger);


scheduler.scheduleJob(jobDetail4, dtiTrigger);


scheduler.start();


}


}


四、quartz的持久化中的表信息


  quartz有两种存储方式,1.存储到内存中,2.存储到 数据库中;下面是数据库中各表的含义


  qrtz_blob_triggers  :这张表示存储自己定义的trigger,不是quartz自己提供的trigger


  qrtz_calendars    :存储Calendar


  qrtz_cron_triggers  :存储cronTrigger


  qrtz_fired_triggers  :存储触发的Tirgger


  qrtz_job_details    :存储JobDetail


  qrtz_locks       :存储程序中非悲观锁的信息


  qrtz_paused_trigger_grps  :存储已暂停的Trigger组信息


  qrtz_scheduler_state  :存储集群中note实例信息,quartz会定时读取该表的信息判断集群中每个实例的当前状态


  qrtz_simple_triggers  :存储simpleTrigger的信息


  qrtz_simprop_triggers  :储存CalendarIntervalTrigger和DailyTimeIntervalTrigger的信息


  qrtz_triggers  :存储已配置的trigger信息

相关文章
|
Oracle 关系型数据库 数据库
ORA-00932: inconsistent datatypes: expected - got NCLOB【ORA-00932: 数据类型不一致: 应为 -, 但却获得 NCLOB 】【解决办法】
ORA-00932: inconsistent datatypes: expected - got NCLOB【ORA-00932: 数据类型不一致: 应为 -, 但却获得 NCLOB 】【解决办法】
|
存储 Java 调度
技术笔记:quartz(从原理到应用)详解篇(转)
技术笔记:quartz(从原理到应用)详解篇(转)
|
4月前
|
监控 Java 调度
SpringBoot中@Scheduled和Quartz的区别是什么?分布式定时任务框架选型实战
本文对比分析了SpringBoot中的`@Scheduled`与Quartz定时任务框架。`@Scheduled`轻量易用,适合单机简单场景,但存在多实例重复执行、无持久化等缺陷;Quartz功能强大,支持分布式调度、任务持久化、动态调整和失败重试,适用于复杂企业级需求。文章通过特性对比、代码示例及常见问题解答,帮助开发者理解两者差异,合理选择方案。记住口诀:单机简单用注解,多节点上Quartz;若是任务要可靠,持久化配置不能少。
474 4
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
7040 115
|
8月前
|
数据挖掘 数据处理
多模态数据信息提取解决方案评测
多模态数据信息提取解决方案评测
389 7
|
编解码 NoSQL Redis
(十一)Netty实战篇:基于Netty框架打造一款高性能的IM即时通讯程序
关于Netty网络框架的内容,前面已经讲了两个章节,但总归来说难以真正掌握,毕竟只是对其中一个个组件进行讲解,很难让诸位将其串起来形成一条线,所以本章中则会结合实战案例,对Netty进行更深层次的学习与掌握,实战案例也并不难,一个非常朴素的IM聊天程序。
336 3
|
移动开发 小程序 前端开发
uni-app组件样式修改不生效
uni-app组件样式修改不生效
|
存储 SQL Java
分布式任务调度框架(一):Quartz
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。其功能类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能,作为一个优秀的开源调度框架
1165 0
分布式任务调度框架(一):Quartz
|
Java Spring 容器
SpringBoot 使用Quartz执行定时任务对象时无法注入Bean问题
SpringBoot 使用Quartz执行定时任务对象时无法注入Bean问题
614 1
|
SQL API 调度
Springboot2.4.5集成Quartz实现动态任务数据持久化-不怕重启服务
Springboot2.4.5集成Quartz实现动态任务数据持久化-不怕重启服务
780 0