本文欢迎转载,原文地址:http://www.cnblogs.com/DjlNet/p/7572174.htmlhtml
这里相信大部分玩家以前如今都应该有过使用定时器的时候或者需求,例如什么定时发送邮件通知,定时筛选取消客户下单未支付的订单,定时数据备份或者归档清洗什么的诸如此类的需求,都是定时的做用的地方,相似好比: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......java
这里有挺多的关键词构成了整个关系链的组织,俗话说得好,一张图赛过几百上千字,因此这里博主靠着拙劣的构想脑回路简述一下框架带来的大致设计思想。
额,这张图也花了一两小时完成责在概括了关于此框架总体结构和组织构成,从中一些备注和标识能够看出,Quarzt.Net再设计上面仍是中规中矩,能考虑到的都尽量考虑了,例如什么具体执行任务与触发器以及调度器三者分离,实现了组件职责单一原则同时呐又能够复用组件一箭双雕,这样一来开发者就就能够按组件开发自由组合,再者就是运用了些许设计模式以及面向接口抽象编程的思想,设计模式上面的功效就是让代码实现了抽象和细节剥离,也就是封装隔离了变化点,让后面的需求或者变化尽量得能够掌控以助于对总体结构的形成冲击力降到最低,而后基于面向接口编程也使得后期开发者自主实现可替换性的组件替换时有了机会,因此从这个层面看一个框架或者一个组件,就能够大体知道做者当初在设计的时候,为何要这样,以致于这样以后带来的好处,什么扩展性、易用性、灵活性等等也是从中体现的吧....那么在学习了人家的设计思想以后,再看看本身系统的代码可谓是有心无力呀,固然拉老项目当然是如此,不过呢对一我的思想上面的影响效果远大于一行代码来的更加长远,因此面对新项目的时候就可得好好构想一下,运用一下所谓的平衡术( 只有合适的框架没有最好的框架 )....git
这里着重记录一下文档中我的以为比较关键之处,此外会用代码的方式去验证,作到斤斤计较,哈哈,因为2.X与3.X差距仍是有的,可是3.X还没正式版,因此咱们的系统中集成的仍是2.X,那么咱们仍是针对2.X文档来作学习哈,不过3.X文档博主已经撸了一遍了差距甚小,以及使用方式基本类似的,具体看版本迁移中的介绍也行...github
这里在官方文档Job一章中也有详细介绍,可是博主这里用本身的话理解一下,固然英文还能够的童鞋能够去看文档,博主还要翻译插件才能凑合阅读呐...sql
一、关于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的时候,能够考虑借鉴代码本身实现。
二、JobDetail关键属性与Job.Execute异常处理:
(1)当一个jobDetail的job运行执行的时间大于它的trigger触发器的间隔调度时间的时候,就会发生它的上次任务还没运行完,接着又开始了下一个任务,或者多个trigger同时触发执行同一个jobDetail的任务的时候都会形成这个jobDetail的job任务并发执行,经过在YourJob:IJob
YourJob
打上[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:上图基本介绍的差很少了,注意这里有个RepeatForever( 与RequestRecovery不要搞混了咯,2333楼主都看花眼了一不当心... )、以及RepeatCount多是你挺经常使用的属性哦,这里补充一下Priority优先级默认是5,当出现资源争抢的时候例如:线程不够,会按照优先级来分配资源执行,以及每种Trigger有本身对应的熄火指令 Misfire Instructions,就是在调度器关闭或者应用程序结束的时候或者调度线程资源不够的时候会发现Trigger暂时性的熄火,默认状况下直接使用智能策略就好了编程
这里官方对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集群部署【附源码】
这里博主这里就不在啰嗦怎么操做了(上文有仔细操做),直接去作实验验证便可,实验以前先说几个注意事项:
一、在持久化模式下面,当Scheduler调度器老是执行和线程池至关的任务job数的时候,数据库链接数尽可能要保证是 ThreadPool线程数量+1 的状态,前提是没超过最大链接数
二、在集群的状况下,当jobDetail设置为 RequestsRecovery -> true 当前的JobDetail才有效果
三、集群配置:除了线程池数量,instanceId能够不一样外,各个节点的配置必须是同样的
四、集群中节点的系统时间一致
五、注意持久化和集群模式下的配置项
在通过一番测试集群发现,Quartz.Net会自动冲裁不可用的节点,让一个可用节点来执行,内部有机制去测试每一个节点的可用状况会定时去检测而后剔除,以及新晋节点的加入等,也正好体现了所谓的负载均衡和故障转移咯,博主测试环境:应用程序Console、数据库Sql server localdb,本机附带两个应用实例测试,发现切换节点时间差为16s...windows
CrystalQuartz : github地址:https://github.com/guryanovev/CrystalQuartz 至于集成方式、方法项目地址当中有说明拉...设计模式
哇,不知不觉竟然还花了挺多的时间去学习与研究这个框架,先是了解生态圈中的定时器框架比较流行的(固然也包含了Hangfire后面咱们有机会也去学习学习),而后想着以前对Quartz有点懵懵懂懂的,因此就去系统的看了看官方文档了解了解人家的设计思想和实现,到了后面带着问题去看了些源代码哈,而后跟着官方的文档基本撸了一遍以后,包含了去园中也去阅读了其余博主的相关博客,以及周边衍生物的 CrystalQuartz UI控制台界面等等。
最后呐,各位看官老爷以为还能够的话,您的评论和点赞都是对博主的确定支持或者斧正!!!哈哈,博主会接着继续框架学习与研究系列....api
关于评论同窗的问题,作了相应的回答和测试,本着咱们打破砂锅问到底的精神,咱们继续来测试研究一下IScheduler的API方法,其实大体看了一下API文档(2.x):http://quartznet.sourceforge.net/apidoc/2.0/html/html/edbcd9ad-0bf8-2b0e-52c9-e8a62ac4f610.htm 直接关键词搜索: IScheduler 便可,而后选择Method就能够看到不少API了找个仍是比较好的且文档的注释其实就是最好的解释和说明了哈,就不用去github找了哈,这个文档也是自动生成的哦,多一句废话....而后什么暂停删除重启jobdetail、trigger都有说明的,这里博主总结一下就是:核心对象是jobdetail对象实例,trigger都是围绕它的,其次才是scheduler,而后jobdetail实质逻辑是IJob实例承载,因此这样一来对jobdetail的删除暂停都会影响到对应的trigger,可是一个jobdetail被多个trigger触发时,某个trigger暂停对jobdetail来讲是透明不知道的,这点从一开始的组件分离设计也体现出来了,因此搞清楚了它们的关系也就天然明白了调度是谁与谁之间的相互关系、相互制约了哈,时间不早了,各位晚安!!!
一、关于使用IScheduler的API:PauseJob 暂停任务以后,下次再重启 ResumeJob 的时候,中途的间隔时间差,将会由该 JobDetial 所从属的 Triggers 去补偿,前提是这些 Triggers 还有执行的机会,例如 RepeatForever计算自身的Interval时间间隔与JobDetail中断间隔的时间倍数自觉补偿丢失执行的次数 或者 RepeatCount 还有剩余次数的状况,将会消耗剩余次数去弥补JobDetail中断的时间差等(注意补偿的时刻:是JobDetail一旦重启的那个时刻);同理还存在 PauseTrigger 暂定某个触发器以后,下次再启动 ResumeTrigger 该触发器的时候,中间间隔的时间差,若是该 Trigger 还有机会执行,将会去弥补丢失的次数,一样是举例:RepeatForever经过自身的Interval时间间隔与时间差比较而后执行须要弥补的次数 或者 RepeatCount 还有剩余次数的状况,将会消耗剩余次数去弥补时间差(注意补偿的时刻:是该Trigger一旦重启的那个时刻)。以上所述呐,博主相继实验上述的状况状况属实,这个应该框架自己一种约定或者意识补偿机制,而且有一位园友也发现了提醒了博主,谢过......再次补充楼主再次想到 [DisallowConcurrentExecution] 可能会有影响,再次把上面的测试再来了一发,结果依然如此,那就是框架已经控制了,测试途中发如今补偿的时间点方面,基于 RAMJobStore 状况下,补偿激发时刻时间精度十分标准的让人难以置信,集中激发次数的时刻都几乎一致,博主这里测试的是 {DateTime.Now:yyyy-MM-dd HH:mm:ss.ffffff}
ffffff 几乎一致,最后几位稍微有些差值,根据测试当时状况而定.....23333
毕个例子上述某种状况,测试看下图(注意红框部分既是激发时刻与补偿次数):
我去,时间又不早了,该休息了,每日一学,千里之行始于足下!!!
一、关于使用winservice来做为定时器quartz.net的宿主程序,这里社区提供了Topshlf(https://github.com/Topshelf/Topshelf)以及对应quartz.net的扩展库Topshelf.Integrations(https://github.com/dtinteractive/Topshelf.Integrations/),至于用不用以及用不用得上那就看本身了,只是说这有些解决方案而已,再者TopShlf确实方便灵活包括部署编写和使用,读者可自行去wiki跟着看看大体有个了解,作到心中有数,使用winservice部署还能够必定程序上的解决了重启的问题,就不会像iis同样有回收须要重启定时器的问题,也算是按期器不较好的宿主方式了吧
二、那么在不一样宿主的状况下,咱们想经过UI界面观察和了解当前调度器的任务和触发器的状况,以及自己的大体状况有一个了解,该怎么办嗯,这里社区是万能的,哈哈咱们只是代码的搬运工和使用方,提供了CrystalQuartz(具体看详情连接:https://github.com/guryanovev/CrystalQuartz)连接内容简直不能太详细了,具体能够看demo:https://github.com/guryanovev/CrystalQuartz/tree/master/examples 介绍了各类状况下面的解决方案,大致有下面几种方式:CrystalQuartz.Simple、CrystalQuartz.Remote、CrystalQuartz.Owin 三种方式,相信在实际运用过程当中,应该有你想要使用的场景吧
关于Quartz.Net自己及其周边就说到这里,期间也更新了几回,哈哈,因为后面去了解周边才有所了解去看来wiki和一些教程,由此在这里再次记录一下以此备份或者加深印象。好的,接下来将会对一个框架及其周边衍生物作出如同的学习和探究.......
Quartz.Net 做者已经更新了支持.net standrad2.0了,就意味着能够在.net core平台使用了,虽然发布的beta版本可是下载数量已经有了好几百,下载地址:https://www.nuget.org/packages/Quartz/3.0.0-beta1,更新说明地址:https://www.quartz-scheduler.net/2017/10/08/quartznet-3.0-beta1-released.html,相信不久以后就会发布release版本,届时.net core框架开发就不怕没有定时器的支持了,~( ̄▽ ̄~)(~ ̄▽ ̄)~