【框架學習與探究之定時器--Quartz.Net 】

聲明


本文歡迎轉載原文地址: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


Quartz.Net框架設計概覽

這裏有挺多的關鍵詞構成了整個關係鏈的組織,俗話說得好,一張圖賽過幾百上千字,因此這裏博主靠着拙劣的構想腦回路簡述一下框架帶來的大致設計思想。

額,這張圖也花了一兩小時完成責在概括了關於此框架總體結構和組織構成,從中一些備註和標識能夠看出,Quarzt.Net再設計上面仍是中規中矩,能考慮到的都儘量考慮了,例如什麼具體執行任務與觸發器以及調度器三者分離,實現了組件職責單一原則同時吶又能夠複用組件一箭雙鵰,這樣一來開發者就就能夠按組件開發自由組合,再者就是運用了些許設計模式以及面向接口抽象編程的思想設計模式上面的功效就是讓代碼實現了抽象和細節剝離,也就是封裝隔離了變化點,讓後面的需求或者變化儘量得能夠掌控以助於對總體結構的形成衝擊力降到最低,而後基於面向接口編程也使得後期開發者自主實現可替換性的組件替換時有了機會,因此從這個層面看一個框架或者一個組件,就能夠大體知道做者當初在設計的時候,爲何要這樣,以致於這樣以後帶來的好處,什麼擴展性、易用性、靈活性等等也是從中體現的吧....那麼在學習了人家的設計思想以後,再看看本身系統的代碼可謂是有心無力呀,固然拉老項目當然是如此,不過呢對一我的思想上面的影響效果遠大於一行代碼來的更加長遠,因此面對新項目的時候就可得好好構想一下,運用一下所謂的平衡術( 只有合適的框架沒有最好的框架 )....git


Quartz.Net文檔劃重點與驗證

這裏着重記錄一下文檔中我的以爲比較關鍵之處,此外會用代碼的方式去驗證,作到斤斤計較,哈哈,因爲2.X與3.X差距仍是有的,可是3.X還沒正式版,因此咱們的系統中集成的仍是2.X,那麼咱們仍是針對2.X文檔來作學習哈,不過3.X文檔博主已經擼了一遍了差距甚小,以及使用方式基本類似的,具體看版本遷移中的介紹也行...github


Job注意事項

這裏在官方文檔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: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:上圖基本介紹的差很少了,注意這裏有個RepeatForever( 與RequestRecovery不要搞混了咯,2333樓主都看花眼了一不當心... )、以及RepeatCount多是你挺經常使用的屬性哦,這裏補充一下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集羣部署【附源碼】
這裏博主這裏就不在囉嗦怎麼操做了(上文有仔細操做),直接去作實驗驗證便可,實驗以前先說幾個注意事項:
一、在持久化模式下面,當Scheduler調度器老是執行和線程池至關的任務job數的時候,數據庫鏈接數儘可能要保證是 ThreadPool線程數量+1 的狀態,前提是沒超過最大鏈接數
二、在集羣的狀況下,當jobDetail設置爲 RequestsRecovery -> true 當前的JobDetail纔有效果
三、集羣配置:除了線程池數量,instanceId能夠不一樣外,各個節點的配置必須是同樣的
四、集羣中節點的系統時間一致
五、注意持久化和集羣模式下的配置項

在通過一番測試集羣發現,Quartz.Net會自動衝裁不可用的節點,讓一個可用節點來執行,內部有機制去測試每一個節點的可用狀況會定時去檢測而後剔除,以及新晉節點的加入等,也正好體現了所謂的負載均衡和故障轉移咯,博主測試環境:應用程序Console、數據庫Sql server localdb,本機附帶兩個應用實例測試,發現切換節點時間差爲16s...windows


Quartz.Net UI控制檯

CrystalQuartz : github地址:https://github.com/guryanovev/CrystalQuartz 至於集成方式、方法項目地址當中有說明拉...設計模式


總結

哇,不知不覺竟然還花了挺多的時間去學習與研究這個框架,先是瞭解生態圈中的定時器框架比較流行的(固然也包含了Hangfire後面咱們有機會也去學習學習),而後想着以前對Quartz有點懵懵懂懂的,因此就去系統的看了看官方文檔瞭解瞭解人家的設計思想和實現,到了後面帶着問題去看了些源代碼哈,而後跟着官方的文檔基本擼了一遍以後,包含了去園中也去閱讀了其餘博主的相關博客,以及周邊衍生物的 CrystalQuartz UI控制檯界面等等。
最後吶,各位看官老爺以爲還能夠的話,您的評論和點贊都是對博主的確定支持或者斧正!!!哈哈,博主會接着繼續框架學習與研究系列....api


更新(2017年9月26日00:36:19)

關於評論同窗的問題,作了相應的回答和測試,本着咱們打破砂鍋問到底的精神,咱們繼續來測試研究一下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來講是透明不知道的,這點從一開始的組件分離設計也體現出來了,因此搞清楚了它們的關係也就天然明白了調度是誰與誰之間的相互關係、相互制約了哈,時間不早了,各位晚安!!!


後續補充(2017年9月27日00:34:17)

一、關於使用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
畢個例子上述某種狀況,測試看下圖(注意紅框部分既是激發時刻與補償次數):

我去,時間又不早了,該休息了,每日一學,千里之行始於足下!!!


更新(2017年10月6日11:29:25)

一、關於使用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和一些教程,由此在這裏再次記錄一下以此備份或者加深印象。好的,接下來將會對一個框架及其周邊衍生物作出如同的學習和探究.......


二度更新(2017年10月17日20:03:15)

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框架開發就不怕沒有定時器的支持了,~( ̄▽ ̄~)(~ ̄▽ ̄)~

相關文章
相關標籤/搜索