复习内容

1. 定时任务在企业级开发中的常见应用

在应用 Quartz 进行企业级的开发时,有一些问题会经常遇到。本节笔者根据自己在项目开发中的经验,介绍企业开发中常见的一些问题以及通常的解决办法。

应用一:如何使用不同类型的 Trigger

前面我们提到 Quartz 中四种类型的 Trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger, 和 NthIncludedDayTrigger。

SimpleTrigger 一般用于实现每隔一定时间执行任务,以及重复多少次,如每 2 小时执行一次,重复执行 5 次。SimpleTrigger 内部实现机制是通过计算间隔时间来计算下次的执行时间,这就导致其不适合调度定时的任务。例如我们想每天的 1:00AM 执行任务,如果使用 SimpleTrigger 的话间隔时间就是一天。注意这里就会有一个问题,即当有 misfired 的任务并且恢复执行时,该执行时间是随机的(取决于何时执行 misfired 的任务,例如某天的 3:00PM)。这会导致之后每天的执行时间都会变成 3:00PM,而不是我们原来期望的 1:00AM。

CronTirgger 类似于 LINUX 上的任务调度命令 crontab,即利用一个包含 7 个字段的表达式来表示时间调度方式。例如,"0 15 10 * * ? *" 表示每天的 10:15AM 执行任务。对于涉及到星期和月份的调度,CronTirgger 是最适合的,甚至某些情况下是唯一选择。例如,"0 10 14 ? 3 WED" 表示三月份的每个星期三的下午 14:10PM 执行任务。读者可以在具体用到该 trigger 时再详细了解每个字段的含义。

DateIntervalTrigger 是 Quartz 1.7 之后的版本加入的,其最适合调度类似每 N(1, 2, 3...)小时,每 N 天,每 N 周等的任务。虽然 SimpleTrigger 也能实现类似的任务,但是 DateIntervalTrigger 不会受到我们上面说到的 misfired 任务的影响。另外,DateIntervalTrigger 也不会受到 DST(Daylight Saving Time, 即中国的夏令时)调整的影响。笔者就曾经因为该原因将项目中的 SimpleTrigger 改为了 DateIntervalTrigger,因为如果使用 SimpleTrigger,本来设定的调度时间就会由于 DST 的调整而提前或延迟一个小时,而 DateIntervalTrigger 不会受此影响。

NthIncludedDayTrigger 的用途比较简单明确,即用于每隔一个周期的第几天调度任务,例如,每个月的第 3 天执行指定的任务。

除了上面提到的 4 种 Trigger,Quartz 中还定义了一个 Calendar 类(注意,是 org.quartz.Calendar)。这个 Calendar 与 Trigger 一起使用,但是它们的作用相反,它是用于排除任务不被执行的情况。例如,按照 Trigger 的规则在 10 月 1 号需要执行任务,但是 Calendar 指定了 10 月 1 号是节日(国庆),所以任务在这一天将不会被执行。通常来说,Calendar 用于排除节假日的任务调度,从而使任务只在工作日执行。

应用二:使用有状态(StatefulJob)还是无状态的任务(Job)

在 Quartz 中,Job 是一个接口,企业应用需要实现这个接口以定义自己的任务。基本来说,任务分为有状态和无状态两种。实现 Job 接口的任务缺省为无状态的。Quartz 中还有另外一个接口 StatefulJob。实现 StatefulJob 接口的任务为有状态的,上一节的简单实例中,我们定义的 SampleJob 就是实现了 StatefulJob 接口的有状态任务。下图列出了 Quartz 中 Job 接口的定义以及一些自带的实现类:

图 14. Quartz 中 Job 接口定义

无状态任务一般指可以并发的任务,即任务之间是独立的,不会互相干扰。例如我们定义一个 trigger,每 2 分钟执行一次,但是某些情况下一个任务可能需要 3 分钟才能执行完,这样,在上一个任务还处在执行状态时,下一次触发时间已经到了。对于无状态任务,只要触发时间到了就会被执行,因为几个相同任务可以并发执行。但是对有状态任务来说,是不能并发执行的,同一时间只能有一个任务在执行。

在笔者项目中,某些任务需要对数据库中的数据进行增删改处理。这些任务不能并发执行,否则会造成数据混乱。因此我们使用 StatefulJob 接口。现在回到上面的例子,任务每 2 分钟执行一次,若某次任务执行了 5 分钟才完成,Quartz 会怎么处理呢?按照 trigger 的规则,第 2 分钟和第 4 分钟分别会有一次预定的触发执行,但是由于是有状态任务,因此实际不会被触发。在第 5 分钟第一次任务执行完毕时,Quartz 会把第 2 和第 4 分钟的两次触发作为 misfired job 进行处理。对于 misfired job,Quartz 会查看其 misfire 策略是如何设定的,如果是立刻执行,则会马上启动一次执行,如果是等待下次执行,则会忽略错过的任务,而等待下次(即第 6 分钟)触发执行。

读者可以在自己的项目中体会两种任务的区别以及 Quartz 的处理方法,根据具体情况选择不同类型的任务。

应用三:如何设置 Quartz 的线程池和并发任务

Quartz 中自带了一个线程池的实现:SimpleThreadPool。类如其名,这只是线程池的一个简单实现,没有提供动态自发调整等高级特性。Quartz 提供了一个配置参数:org.quartz.threadPool.threadCount,可以在初始化时设定线程池的线程数量,但是一次设定后不能再修改。假定这个数目是 10,则在并发任务达到 10 个以后,再有触发的任务就无法被执行了,只能等待有空闲线程的时候才能得到执行。因此有些 trigger 就可能被 misfire。但是必须指出一点,这个初始线程数并不是越大越好。当并发线程太多时,系统整体性能反而会下降,因为系统把很多时间花在了线程调度上。根据一般经验,这个值在 10 -- 50 比较合适。

对于一些注重性能的线程池来说,会根据实际线程使用情况进行动态调整,例如初始线程数,最大线程数,空闲线程数等。读者在应用中,如果有更好的线程池,则可以在配置文件中通过下面参数替换 SimpleThreadPool:org.quartz.threadPool.class = myapp.GreatThreadPool。

应用四:如何处理 Misfired 任务

在 Quartz 应用中,misfired job 是经常遇到的情况。一般来说,下面这些原因可能造成 misfired job:

1)系统因为某些原因被重启。在系统关闭到重新启动之间的一段时间里,可能有些任务会

被 misfire;

2)Trigger 被暂停(suspend)的一段时间里,有些任务可能会被 misfire;

3)线程池中所有线程都被占用,导致任务无法被触发执行,造成 misfire;

4)有状态任务在下次触发时间到达时,上次执行还没有结束;

为了处理 misfired job,Quartz 中为 trigger 定义了处理策略,主要有下面两种:

MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:针对 misfired job 马上执行一次;

MISFIRE_INSTRUCTION_DO_NOTHING:忽略 misfired job,等待下次触发;

建议读者在应用开发中,将该设置作为可配置选项,使得用户可以在使用过程中,针对已经添加的 tirgger 动态配置该选项。

2.

时间: 2024-11-06 09:52:16

复习内容的相关文章

蓝懿 iOS 复习内容 数组 等

又逢周六,我感觉最近这3天学习的东西挺多的,但是今天来上自习的人刚觉刚刚一半左右,也许是由于这两周大家都没有休息时间的原因吧!但时我自己对这几天所学的东西也不是特别把握,今天看了一天也没能完全系统性的掌握,没有把书读薄了的感觉: 说一下今天复习的内容吧: 1.主要复习了字符串的一些基本内容:创建.拼接.截取.地址拼接.替换.包含查询.编解码.可变字符串.判断相等[p2 isEqual:p]: 2.Categroy:创建与使用 3.数组.可变数组.创建.导入.导出,遍历: 4.基本数据类型和结构体

课堂复习内容(2)

选择结构 if:如果    else:否则    break:跳出     continue:持续     match:匹配 gender:性别     random:随机数      score:成绩 示例:1 public class Demo{ public static void main(String[]args){ int  score=91;                     //可以把score写成输入;(详情在复习(1)) if(score>=90){ System.ou

7.17复习内容

# !/usr/bin/env python # !--*--coding:utf-8 --*-- # [email protected] :2018/7/17 20:32 # [email protected] TrueNewBee # 2018-7-17 20:57:16 # 复习: # 信号量 Semaphore # from multiprocessing import Semaphore # 用锁的原理实现的,内置了一个计数器 # 在同一时间,只能有指定数量的进程执行某一段被控制的代码

css复习内容

有时候 自己动手写一遍比想十遍都有用 <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title></title> <style> .in{ width: 120px; height: 40px; border: 2px solid black; text-align: center; line-height: 40px; border-radi

近期待复习内容

1.MyBatis的动态SQL详解 2.MyBatis学习之三.动态SQL语句 3.Spring定时器的配置(注解+xml)方式 4.Spring任务调度器之Task的使用 5.spring task定时器的运用 6.Spring定时任务的几种实现 7.spring cron表达式

课堂复习内容(1)

扫描仪类: import java.util.Scanner; Scanner input=new Scanner(System.in); 示例: import java.util.Scanner; public class Test02 {                   public static void main(String[] args) {                    Scanner input = new Scanner(System.in);           

初级复习内容

1.局部变量(lacal variable):方法或语句块内部定义的变量.生命周期是从声明位置开始到”}”为止.在使用前必须先声明和初始化(赋初值). 2.成员变量和类变量初始值 {\u后面加上十六进制代码来表示Unicode字符. JAVA没有直接用“\u”的而是“\u5845”,u后的4位数均为16进制数,这样表示一个字符}; 数值型变量初始化成 0 或 0.0,字符型变量的初始化值是 16 位的 0.(‘\u0000’),布尔型默认是 false 3.基本数据类型(逻辑型+文本型+数值型)

Java总复习内容

StringBuffer定义时需要用正确的方式 例如: StringBuffer xxx = new StringBuffer("雯雯是猪"); 使用StringBuffer的连接和追加方法时要用一个返回值进行接受: String方法: s.equalslgnorecase()          //         无区分大小写 s.tolowercase()                  //          转化为小写 s.touppercase()            

一周复习总结(三)第四周

复习内容: 第四章: 第一节 资本主义的形成及以私有制为基础的商品经济的矛盾 第二节 资本主义经济制度的本质  第三节 资本主义的政治制度和意识形态 第五章: 第一节 从自由竞争资本主义到垄断资本主义  第二节 当代资本主义的新变化  第三节 资本主义的历史地位和发展趋势 知识点总结: 商品经济得以产生的社会历史条件有两个:一是存在社会分工,二是生产资料和劳动产品属于不同的所有者. 商品具有使用价值和价值两个因素或两种属性:①使用价值反映的是人与自然之间的物质关系,是商品的自然属性,使用价值构成