• 1. Spring 3.0 作业调度简介作者:十二郎
  • 2. Contents正文2参考资料4预备知识31延伸33
  • 3. 什么是作业调度“作业”这一技术术语,来源于大型机时代。彼时,用户提交一叠带有作业内容描述的穿孔卡片或纸带给操作人员,由操作人员帮忙执行那些作业。等作业执行完毕后,用户再回到主机那边取回自己的卡片或者纸带和打印出来的输出结果。因为不是每一个作业都要求被立即执行,所以作业可以被安排在将来的某个时段执行。 例子:一个系统管理员每天晚上可能有一份要执行的作业列表: · 10:00 PM: 运行患者信息文件的上载作业 · 11:00 PM: 运行销售数据报表生成 · 11:59 PM: 进行数据库的备份 作业调度通常是指运行一个批量的作业。这种批处理作业一般都在后台运行且不需要与用户进行交互。现在,显著增多的多样性任务已代替了早先的批处理作业。在一些大的企业中每小时跑上百个作业已属普遍,并且作业的规模与复杂性仍在持续上扬,因此作业调度器也随需而生。预备知识
  • 4. 预备知识为什么要用作业调度更少的 出错几率更强的 可伸缩性我们也许能在一个小时内手工完成十几二十个作业,但是随着每小时处理作业数的增加,我们很难杜绝不在作业处理中引入错误。如果借助于作业调度,则只会受到硬件资源的影响了。更高的资源 使用效率人犯错误的频度是远高于计算机的。把一系列的任务自动加入到一个作业中,然后通过调度器在预期的时间节点自动执行这个作业,相对于人的手工处理,我们可以减少大多数的出错机会。俗话说,时间就是金钱,将大量的资源投入到枯燥而重复的任务中无疑是对金钱和资源的浪费。而任务规模越大、越复杂、越频繁地被执行,我们从自动化流程的获益也越大。
  • 5. Java SE中的作业调度预备知识JDK1.3工具包java.util引入的Timer及 TimerTask这两个类可以实现一个最基本的调度器,但还不足以构建一个真正的作业调度器。任何严格意义上的作业调度器都提供直接指定执行时间、以多种方式存储作业信息、利用API进行定制开发等功能。Timer类无法对作业和触发器作相应的组织,使用每任务一个线程而不是线程池的方式以及其它的一些不足之处都使其难以成为一个完全意义的作业调度器。 JDK1.5并发工具包java.util.concurrent引入的定时调度接口ScheduledExecutorService及实现类ScheduledThreadPoolExecutor解决了Timer类在线程处理的不足,灵活性和功能都优于Timer类,但仍然不是一个真正的作业调度器。
  • 6. Java 作业调度框架预备知识Quartz(http://www.quartz-scheduler.org) Flux Scheduler(http://www.fluxcorp.com/) jcrontab(http://jcrontab.sourceforge.net/index.shtml) 更多Java开源调度框架参见http://www.open-open.com/14.htm。
  • 7. Spring 2.x对作业调度的整合预备知识2.x系列包含了对定时调度服务Timer及Quartz的内置支持类。二者可以通过FactoryBean分别指向Timer或Trigger实例的引用进行配置。 更进一步,有个对Quartz和Timer都有效的工具类可以让你调用目标对象的某个方法(类似通常的MethodInvokingFactoryBean操作)。 Spring还包含有用于线程池调度的类,针对Java1.3、1.4、1.5及JavaEE环境的差异都进行了抽象,隐藏了具体的实现细节。
  • 8. 整合示例
  • 9. 整合示例
  • 10. Spring3.0新特性预备知识声明式的Model验证 支持自动探测Hibernate Validator,设置为对JSR303注解验证规范的实现 全面的REST支持 包括服务端和客户端,提供了RestTemplate支持全功能的REST客户端 支持嵌入式数据库 自带嵌入式数据库支持,方便测试和演示 对象XML映射 提供JAXB、XmlBeans及Xstream等方式的OXM实现 下一代作业调度支持 新的内置TaskScheduler和Trigger机制提供完善的cron支持核心API迁移至Java1.5并使用泛型 完全基于Java5构建,很多接口增加了泛型的支持 Spring Expression Language 支持在配置及注解中使用表达式语言,不再是简单的属性文件变量 JavaConfig 支持使用Java类和方法来定义容器和Bean 类型转换及格式化显示 加入了一个通用的类型转换系统及类似JSR303风格的格式化系统
  • 11. Spring3.0 框架结构预备知识
  • 12. 正 文正文Spring3.0 内置对作业调度的支持并不是想完全替代Quartz这样的全功能型作业调度框架。Spring3.0内置的定时调度的功能可以通过注解及XML配置来实现。Sping3Demo项目的com.chinadim.demo.spring.schedule包路径下的两个示例程序尽可能简洁地演示了怎样使用这两种方式。根据调查,大多数使用Quartz的用户只是为了利用其支持cron表达式的便利,而不会涉及到集群及持久化存储作业这样的高级特性。Spring框架内置调度的目的就是为了覆盖这80%的使用场景,并增加了诸如异步执行返回Future结果等实用的功能点,方便广大用户的使用。
  • 13. 正文 正 文首先我们来看用注解的方式是如何实现的。通过AnnotationDemo的main方法可以直接运行该示例程序。程序代码看起来再简单不过的Spring ApplicationContext初始化调用:之所以如此简单是因为ApplicationContext包含了一个主动激活的组件,由于该组件的缘故,main方法便不会退出。调度配置文件schedule-annotation.xml也很简短,只包含两个标签:
  • 14. 正文 正 文component-scan标签的base-package属性声明了bean所在的包路径,其下有两个bean元素:AsyncWorker、ScheduledProcessor。 ScheduledProcessor类在接口方法上使用了@Scheduled注解,因此成为前面提及到的主动激活组件。配置文件中的annotation-driven标签会使用一个Spring的TaskScheduler实例注册此方法并以30秒的固定延迟执行它:在详述这两个bean之前,我们先来关注下annotation-driven标签。这是Spring3.0新引入的标签之一,实现了对@Scheduled及@Async注解的驱动。我们可以通过属性scheduler及executor来分别引用Spring的TaskScheduler和TaskExecutor。本例中使用默认配置即可。
  • 15. 正文 正 文此段代码中,Worker对象被循环调用,其实现类AsyncWorker在接口方法work()上使用了@Async注解,在annotation-driven标签的作用下,该类被代理所包装, work()方法实际上是被一个TaskExecutor实例调用的。为了验证这一点,我们在方法内打印了当前线程的别名。此外,每次方法调用都使用了线程休眠操作以模拟执行方法所消耗的时间,这样,可以明确该操作是被并发地调用的:
  • 16. 正文 正 文运行AnnotationDemo将会输出类似下面的结果:
  • 17. 正文 正 文输出结果中有几处是我们需要注意的: 首先,每30秒会一次性打印出10行处理开始的信息(@Scheduled的作用); 其次,这些操作是被不同的线程并发地调用的(@Async的作用)。 最后一条beginning work信息与第一条completed work信息之间应该有大约5秒的间隔。假如这些操作是在单线程环境下执行的,我们将会看到处理开始与操作结束的信息会成对且连续地打印出来,整个处理流程会花费大约50秒的时间:
  • 18. 正文 正 文 接下来,我们来看下不使用注解的XML配置实现方式。示例程序包含有另外一个在其process()方法上不含@Scheduled注解的类SimpleProcessor:
  • 19. 正文 正 文XML配置方式看起来并不比注解方式冗长多少:因为Spring3.0提供了task命名空间以保持配置的简洁明了:运行XmlDemo的main()方法,会看到process()操作每隔10秒执行一次。 为多样化起见,该示例使用了cron表达式而不是简单的固定延迟。此处可能会注意到定时间隔是以3秒的偏移量为基准的(3:,13:,23:等等),但cron表达式提供的功能远不止于此(这里只是不想创建一个只在某些天或特定时段运行的示例而已)。
  • 20. 延伸 延 伸前面曾经提到,Spring3.0还提供了异步执行返回Future结果的功能。下面结合呼叫中心预呼叫子系统的实际需求来说明如何利用该项特性。 预呼叫系统主要用于对从CSR系统传递过来的客户号码集,通过一定的算法进行批量自动外拨,检测到对方应答电话后在极短的时间内将电话转至空闲的坐席以达到提高工作效率的目的。而主流呼叫中心产品采用的ErlangC公式则可以用作计算批量外拨数量的高效算法:
  • 21. 延伸 延 伸以此算法计算外呼数量时,由于暂时无法得出逆向求解的可行算法,故而采用穷举法利用计算机运算速度的优势来求解。
  • 22. 延伸 延 伸示例程序代码在Spring3Demo项目的code.kanine.showcase.predial包路径下,启动类依然是简单的Spring ApplicationContext初始化调用:
  • 23. 延伸 延 伸预呼叫处理业务实现类的代码如下:
  • 24. 延伸 延 伸这里使用到了Future对象。那什么是Future呢?Future表示异步运算的结果,它提供了检查运算是否完成的方法,并获取运算的结果。运算完成后只能使用get()方法来获取结果,如有必要,运算完成前可以阻塞此方法。取消运算则由cancel()方法来执行。还提供了其它的方法以确定任务是正常完成还是被取消。异步求解ErlangC公式的代码如下:
  • 25. 延伸 延 伸XML配置文件比前面的两个示例都要全面一些:与集成Quartz的配置文件相比,Spring3.0内置的调度配置相当简洁。
  • 26. 参考资料参考资料1.Spring Framework Reference Documentation 2.Task Scheduling Simplifications in Spring 3.0 3.ThreadPoolExecutor 4.Future 5.ErlangC计算公式 6.Quartz Job Scheduling Framework
  • 27. 欢迎提问!Thank You !