【框架学习与探究之定时器--Quartz.Net 】

声明


本文欢迎转载原文地址:http://www.cnblogs.com/DjlNet/p/7572174.html


前言

这里相信大部分玩家之前现在都应该有过使用定时器的时候或者需求,例如什么定时发送邮件通知,定时筛选取消客户下单未支付的订单,定时数据备份或者归档清洗什么的诸如此类的需求,都是定时的作用的地方,类似比如:Windows的计划任务、数据库的计划任务都是同样体现,那么相关于这方面的轮子或者发动机就孕育而生了,也有一直一来没怎么人使用的微软框架自带5种Timer系列等等......
额,关于今天要说的这个定时器框架,相信无论是Java还是.net开发者都肯定听说过了,当然这个定时器有些年生了是个老字号了并不表示它就真的老了哦,去github:https://github.com/quartznet/quartznet看了才知道,虽然是移植过来的但是作者是一个人在维护呀,还很积极在commit和解答issues,这里还是佩服一下作者的勇气和那份责任,顺手Star一下加加油,而且最新的3.x也正在开发基于.net标准的做新的nuget package https://www.nuget.org/packages/Quartz/3.0.0-alpha3 开发,这里博主大致使用一下主要有一个亮点是把以前的依赖包都整合了,体现就是一个nuget包就OK了看不见什么Common.Logging之类的了,其中还有些Feature或者Fixes(见3.X文档),期待稳定正式版吧!!!...
然后关于这个框架的自述或者介绍什么的,园子文章很多啦或者官方文档https://www.quartz-scheduler.net介绍的都挺清楚的,其中文档中有12个学习课程(其中发现园友对文档的中文翻译,把园中关于此框架的部分文章看了一下,具体想去了解关键字搜索即可,不过较多是大致介绍和简单使用的居多,部分都是自我集成在自己或者公司的系统中去了,形成系统中的一环或者整个任务调度平台覆盖到系统层面之上,所以看如何设计整合和正确姿势使用才是关注的重点嘛!!),全部刷一遍也费不了多少时间的,也就是少几集电视嘛,然后这样就会对框架设计或者学习使用都基本有个大致了解,后面催牛的时候不至于让人家以为你在瞎BB呐,哈哈。那么,今天我们写文是为了记录什么呐?额,主要是博主本身在学习过程的遇到一些不解或者需要划重点的东西记录一下吧,233333......


Quartz.Net框架设计概览

这里有挺多的关键词构成了整个关系链的组织,俗话说得好,一张图胜过几百上千字,所以这里博主靠着拙劣的构想脑回路简述一下框架带来的大体设计思想。

额,这张图也花了一两小时完成责在归纳了关于此框架整体结构和组织构成,从中一些备注和标识可以看出,Quarzt.Net再设计上面还是中规中矩,能考虑到的都尽可能考虑了,例如什么具体执行任务与触发器以及调度器三者分离,实现了组件职责单一原则同时呐又可以复用组件一举两得,这样一来开发者就就可以按组件开发自由组合,再者就是运用了些许设计模式以及面向接口抽象编程的思想设计模式上面的功效就是让代码实现了抽象和细节剥离,也就是封装隔离了变化点,让后面的需求或者变化尽可能得可以掌控以助于对整体结构的造成冲击力降到最低,然后基于面向接口编程也使得后期开发者自主实现可替换性的组件替换时有了机会,所以从这个层面看一个框架或者一个组件,就可以大致知道作者当初在设计的时候,为什么要这样,以至于这样之后带来的好处,什么扩展性、易用性、灵活性等等也是从中体现的吧....那么在学习了人家的设计思想之后,再看看自己系统的代码可谓是有心无力呀,当然拉老项目固然是如此,不过呢对一个人思想上面的影响效果远大于一行代码来的更加长远,所以面对新项目的时候就可得好好构想一下,运用一下所谓的平衡术( 只有合适的框架没有最好的框架 )....


Quartz.Net文档划重点与验证

这里着重记录一下文档中个人觉得比较关键之处,此外会用代码的方式去验证,做到斤斤计较,哈哈,由于2.X与3.X差距还是有的,但是3.X还没正式版,所以我们的系统中集成的还是2.X,那么我们还是针对2.X文档来做学习哈,不过3.X文档博主已经撸了一遍了差距甚小,以及使用方式基本相似的,具体看版本迁移中的介绍也行...


Job注意事项

这里在官方文档Job一章中也有详细介绍,但是博主这里用自己的话理解一下,当然英文还可以的童鞋可以去看文档,博主还要翻译插件才能凑合阅读呐...

1、关于IJob与IJobDetail注意点实现接口IJob的类的实例只是代表了要具体执行的任务逻辑而已,而实现了IJobDetail的类的实例才是包含此任务的细节,通过JobBuilder.Create创建,但是这里的创建也正如上图所诉不是“真正的创建”而是传递了JobType在JobDetial中保存引用而已,而后面具体在scheduler.JobFactory中来接管IJob具体实例的创建工作,这样一来你的ioc容器就可以在自定义的XXXJobFactory中去自定义NewJob的创建过程,注入你的XXXService或者XXXRepository,当然也可以直接使用ServiceLoader.Resolve服务定位器模式在Execute中直接解析拿出来使用也可以,但是注意一点Quartz.Net是每次执行Job的时候都是会新创建一个Job实例的,所以注意ioc容器注入对象的生命周期的合理性。 大致去nuget上面搜索一下(某些包最近commit时间略显久远了),autofac、unity、Ninject都提供了第三方集成Quarzt.Net包,大致看了一下代码量很少只有几个类而已,编写套路大体相近某些还接管了ISchedulerFactory创建等,所以自己需要为Quarzt集成第三方ioc的时候,可以考虑借鉴代码自己实现。
2、JobDetail关键属性与Job.Execute异常处理
(1)当一个jobDetail的job运行执行的时间大于它的trigger触发器的间隔调度时间的时候,就会发生它的上次任务还没运行完,接着又开始了下一个任务,或者多个trigger同时触发执行同一个jobDetail的任务的时候都会造成这个jobDetail的job任务并发执行,通过在YourJob:IJobYourJob打上[DisallowConcurrentExecution],其实就是一个简单的属性类标记一下这个类而已,然后作用于具体对应的JobDetial实例(这句话很关键),然后JobDetail的Job执行逻辑在上述两种情况下都可以按照理想执行了,以上博主已经通过代码验证过了哈,注意:多个trigger绑定同一个jobdetail需要jobDetail->StoreDurably() +trigger-> ForJob()
(2)YourJob标记: [PersistJobDataAfterExecution] 可以记住jobDetail.JobDataMap的值,所以你可以在job的执行中修改它的值,在下一次执行时候可以拿到更新之后的值拉,所以在这种情况下需要记住上次状态,当然就需要 [DisallowConcurrentExecution] 来做支持拉,这个自然是可以理解的
(3)Durability 持久的存储作用于jobdetail,RequestsRecovery 请求恢复作用于jobdetail当出现崩溃类似场景时使用, JobExecutionException 当job执行时需要使用try-catch来截获所有异常,且再次向上次抛出异常需要包装成Quartz认识的异常类型JobExecutionException ,且可以设置JobExecutionException 的可用属性,当然你可以使用BaseJob之类的或者AOP(castle dynamic proxy)的方式来实现job执行当中的日志、异常处理等


Trigger其他说明

Trigger:上图基本介绍的差不多了,这里补充一下Priority优先级默认是5,当出现资源争抢的时候例如:线程不够,会按照优先级来分配资源执行,以及每种Trigger有自己对应的熄火指令 Misfire Instructions就是在调度器关闭或者应用程序结束的时候或者调度线程资源不够的时候会发现Trigger暂时性的熄火,默认情况下直接使用智能策略就行了


JobStores 持久化与 Clustering 集群

这里官方对JobStores提供了两种模式,https://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/job-stores.html,其中一般情况还是推荐使用 RAMJobStore,因为不论是存储拿数据还是CPU级别的调度都是最佳的,但是缺点也是比较明显的就是对需要持久化的信息在应用程序重启之后就丢失了,例如XXJob的上次激发时间最后执行时间等,所以框架自身提供了AdoJobStore 可以找到对应的** DB Provider 配置** 即可,然后 Clustering 集群肯定是基于上面的 AdoJobStore 数据库存储模式下的,可以解决负载均衡和故障转移的功能。
这里博主将会做实验去验证,同时在园子中搜索发现已经有同学去实践了,这里引用一下各位大大的博文地址(应该不会介意,嘻嘻),大家也可以参考学习:
http://www.cnblogs.com/knowledgesea/p/5145239.html Quartz.net持久化与集群部署开发详解
http://www.cnblogs.com/mushroom/p/4231642.html#3760225 Net作业调度(四)—quartz.net持久化和集群
http://www.cnblogs.com/lanxiaoke/p/6629481.html 任务调度之持久化(基于Quartz.net)
http://www.cnblogs.com/lanxiaoke/p/6637714.html 任务调度之集群(基于Quartz.net)
http://www.cnblogs.com/huangxincheng/p/6916246.html 使用sqlserver搭建高可用双机热备的Quartz集群部署【附源码】
这里博主这里就不在啰嗦怎么操作了(上文有仔细操作),直接去做实验验证即可,实验之前先说几个注意事项:
1、在持久化模式下面,当Scheduler调度器总是执行和线程池相当的任务job数的时候,数据库连接数尽量要保证是 ThreadPool线程数量+1 的状态,前提是没超过最大连接数
2、在集群的情况下,当jobDetail设置为 RequestsRecovery -> true 当前的JobDetail才有效果
3、集群配置:除了线程池数量,instanceId可以不同外,各个节点的配置必须是一样的
4、集群中节点的系统时间一致
5、注意持久化和集群模式下的配置项

在经过一番测试集群发现,Quartz.Net会自动冲裁不可用的节点,让一个可用节点来执行,内部有机制去测试每个节点的可用情况会定时去检测然后剔除,以及新晋节点的加入等,也正好体现了所谓的负载均衡和故障转移咯,博主测试环境:应用程序Console、数据库Sql server localdb,本机附带两个应用实例测试,发现切换节点时间差为16s...


Quartz.Net UI控制台

CrystalQuartz : github地址:https://github.com/guryanovev/CrystalQuartz 至于集成方式、方法项目地址当中有说明拉...


总结

哇,不知不觉居然还花了挺多的时间去学习与研究这个框架,先是了解生态圈中的定时器框架比较流行的(当然也包含了Hangfire后面我们有机会也去学习学习),然后想着之前对Quartz有点懵懵懂懂的,所以就去系统的看了看官方文档了解了解人家的设计思想和实现,到了后面带着问题去看了些源代码哈,然后跟着官方的文档基本撸了一遍之后,包含了去园中也去阅读了其他博主的相关博客,以及周边衍生物的 CrystalQuartz UI控制台界面等等。
最后呐,各位看官老爷觉得还可以的话,您的评论和点赞都是对博主的肯定支持或者斧正!!!哈哈,博主会接着继续框架学习与研究系列....

赞 (0) 评论 分享 ()