Quartz.NET 3.0.7 + MySql 動態調度做業+動態切換版本+多做業引用同一程序集不一樣版本+持久化+集羣(一)html
Quartz.NET 3.0.7 + MySql 動態調度做業+動態切換版本+多做業引用同一程序集不一樣版本+持久化+集羣(三)數據庫
Quartz.NET 3.0.7 + MySql 動態調度做業+動態切換版本+多做業引用同一程序集不一樣版本+持久化+集羣(四)api
上篇文章搞定了第一個功能.安全
1.利用反射動態建立Job;服務器
2.調度服務如何知道有新的任務來了?是調度服務輪詢數據庫?仍是管理後臺通知調度服務?又或者遠程代理?負載均衡
3.須要一個管理後臺,提供啓動,暫停,恢復,中止等功能;框架
4.至於集羣,Quartz.NET 自己就提供該功能,只不過要使用它的持久化方案而已.這個點只須要在配置文件上作作手腳就能夠了,並不須要怎麼開發.tcp
5.管理後臺如何實現啓動,暫停,恢復,中止等功能?靠遠程代理?仍是經過其餘方式?ide
接下來解決剩下的問題.post
我一直認爲世間萬物,塵歸塵,土歸土,本質都是同樣的.
動物與動物交流,機器與機器交流,兩個應用程序之間的交流,管你是什麼東西交流,都跟人與人交流同樣.
要麼你不停的問他,要麼你等他告訴你.
再不濟,你倆都看對方不順眼,不想彼此直接交流,因而找來一箇中間人.
他告訴中間人,中間人告訴你,
或者中間人不停的問他,有了消息,中間人再告訴你.
又或者你想要什麼消息了,就去問中間人.中間人告訴你沒有,那就沒有.中間人說"我找一找","誒,這裏有.來給你消息"
我認爲其實就是這麼回事兒,固然,我入行不久,理解還不夠深刻.不過目前我以爲這樣理解能解決問題,就夠了.
學習講究的是方法,一來就研究到最底層,不是明智之舉.等哪天發現這麼理解不能解決問題,這麼理解有問題的時候,再深刻研究也不遲.
仍是那句話,路要一步一步走,飯要一口一口吃.存在的就是合理的.
當咱們在管理後臺新增一個做業的時候,做業的信息,好比名稱,時間表達式,程序集物理路徑,做業類型的徹底限定名等,咱們確定是要找張表單獨存起來的,因此這裏須要新建一張表:
CREATE TABLE `jobinfo` ( `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號', `SchedName` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '調度器名稱', `JobName` varchar(50) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT '做業名稱', `JobGroup` varchar(20) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT '做業組', `Cron` varchar(50) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '時間表達式', `Second` int(11) NOT NULL DEFAULT '0' COMMENT '間隔時間,單位:秒', `AssemblyPath` varchar(250) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT '做業程序集物理路徑', `ClassType` varchar(100) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT '做業徹底限定名', `StartTime` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '做業開始時間', `CreateTime` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '做業建立時間', `ProjectTeam` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '項目組', `IsDeleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否刪除 0:否 1:是', PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
那麼這些數據如何讓調度服務知道呢?(因爲調研 Quartz,NET 框架的時候,看到不少大神說,IIS 回收池有坑.因此我就沒考慮把調度服務和管理後臺集成在一塊兒)
我初版作的輪詢 ,就是在調度服務啓動的時候,啓動一個預先已經建好的輪詢job(輪詢的間隔時間儘可能短一點,調低啞火忍耐時間,設置好失火策略),
輪詢job掃描這張表(下簡稱:做業表),而後根據表裏的某個字段來判斷是否已經啓動了該job.
同時,當Job監聽器監聽到本次輪詢job執行完成後,暫停它,避免無謂的輪詢.
當管理後臺新增了一個做業時,就經過遠程代理對象恢復該輪詢job.
我自覺得這個方案很牛B,或者有那麼一點點小"聰明".
可是,我後來把這個方案幹掉了.
由於要使用遠程代理,管理後臺就必需要 安裝 Quartz.NET ,這一點我感受很不爽.我只想在調度服務一個地方安裝它.
這裏插一句.
爲了實現遠程代理,網上找了好多代碼,各類配置,都失敗了,不知道是我沒copy對,仍是版本問題.
這裏奉上我本身研究出來的,實測可用的代碼.至於遠程代理的配置文件,就不貼出來了,網上太多了.
RemotingSchedulerProxyFactory proxyFactory = new RemotingSchedulerProxyFactory
{
Address = "tcp://127.0.0.1:555/QuartzScheduler" }; var schedulerProxy = proxyFactory.GetProxy();
第二版,也就是目前採用的方案:
在調度服務內利用 owinself 組件內置一個api接口,接收管理後臺的請求,拿到做業的數據後,實現該做業的啓動,暫停,恢復,中止等操做.
所以,管理後臺不須要安裝 Quartz.NET 組件了,只須要操做一下做業表,把做業的信息post給調度服務內置的api接口便可.
整個設計以下:
(03 調度服務框架核心 中的 Middleware 你們能夠不用理它,僅僅是我拿來練手用的)
引用關係以下:
Host 引用 Service ,Service 裏面主要是初始化調度器,啓動API監聽方面的代碼;
Service 引用 Api , Api 接收管理後臺的請求
Api 引用 Logic ,Logic 裏面就是具體的對Job的啓動,暫停,恢復等操做了.
另外, Api , Logic 都須要應用 Model
Logic 還須要引用 BaseJob
管理後臺與調度框架沒半毛錢聯繫.(固然,Model仍是要引用一下)
我的以爲這個設計耦合度比較低了.不過,仍是那句話,任何事物都要辯證來看,耦合度是低了,開發量就相對多了一些.
是時候看看界面了,MVC作的,很清(jian)爽(lou)吧!個人水平實在有限,就這個界面我仍是網上抄的模板,固然也參考了這位前輩對Quartz.NET使用方面的一些思路.原諒我,帖子找不到了....
像不少功能,好比觸發器的觸發機制選擇,執行次數,失火策略等,我就沒有在頁面上體現了,而是在調度框架內部暫時寫死了.一是時間來不及,二是公司的調度任務基本都差很少,沒有什麼大的區別.不過之後確定仍是要加上.好比我只想今天下午執行10次等等
對這個界面作一個簡單的說明:
對於"調度器名稱"字段須要特別說明.
整個框架開發到一半的時候,來了個新需求:
要同時開多個調度服務(控制檯程序)調度不一樣的任務,可是用同一張數據庫表,同一個管理後臺來管理.
好比如今已經啓了一個控制檯程序了,管理了10個任務;
再啓一個控制檯程序,管理另外10個任務,可是管理後臺仍是同一個,數據庫表仍是同一張.注意,不是集羣,只是想分開管理任務而已.
基於這個需求,因此設計了"調度器名稱"字段.
瞭解持久化方案的朋友確定知道,在quartz.config配置文件中有這麼一行:
quartz.scheduler.instanceName = wechat
個人方案就是利用這句配置,
一個控制檯程序(宿主)就是一個調度器,同時,將api地址放到控制檯程序的配置文件中:
<add key="ApiAddress" value="http://localhost:25250" />
當咱們再開一個控制檯程序時,(注意,不是集羣),就須要同時修改上面兩個配置,
好比新的控制檯程序的配置及新的quartz.config以下:
quartz.scheduler.instanceName = refuge <add key="ApiAddress" value="http://localhost:25251" />
那麼,這時候管理後臺就須要增長以下配置了:
<add key="wechat" value="http://localhost:25250" /> <add key="refuge" value="http://localhost:25251" />
當咱們點擊按鈕,發送請求前,先根據這個job的 "調度器名稱" 字段從配置文件中獲取它應該請求的地址.
爲了作到絕對安全,我在控制檯程序的api中添加了過濾器,操做請求過來的時候,檢查傳過來的job數據中的"調度器名稱"是否和該控制檯程序中的調度器名稱同樣.不同則不作任何操做.
效果:
既然說到這個份上了,順便把集羣也說了.配置文件就不貼了,網上太多.
Quartz.NET 的集羣功能究竟是個什麼功能?它實際覆蓋兩個功能:
簡單說就是啓動兩個控制檯程序,做業只會被一個控制檯程序調度,當其中一個掛了,另一個立馬開始工做.
因爲我這個框架內置了api,api監聽地址確定不能重複,因此要使用集羣,必須修改api地址.
那麼在集羣模式下,管理後臺怎麼知道應該請求哪一個api呢?
咱們先看看效果,而後再解釋.
假設如今有兩個控制檯應用程序,配置以下,調度器名稱都叫 "wechat",而且自己已經存在一個Job了.
控制檯1:
<add key="ApiAddress" value="http://localhost:25250" />
控制檯2:
<add key="ApiAddress" value="http://localhost:25260" />
效果圖:
能夠清楚的看到,下面的控制檯程序並無執行Job,如今咱們關掉上面的控制檯,
我是20秒的時候關閉的,過了16秒,下面的控制檯開始執行了.
如今來解釋下,管理後臺的操做到底請求哪一個api.
可能會有朋友認爲,確定要請求 25250 ,由於 25260 的控制檯處於"備用"狀態,請求它沒效果.
事實上,這樣理解是錯的.
就算請求發送到 25260 控制檯,雖然表面上這個控制檯的調度器是處於"備用"狀態,但實際上它只是"待命"而已,有請求過來,它依然能"幹活".
"改革春風吹滿地",實踐是檢驗真理的惟一標準.
仍是上面兩個控制檯,配置文件不變,如今修改一下管理後臺的配置文件,api地址修改成 25260
<add key="wechat" value="http://localhost:25260" />
運行效果:
21:18:00左右的時候 ,我經過管理後臺啓動了 Job2,能夠看到 25260 所在的控制檯開始幹活了.
因此,根本不用擔憂,以集羣的方式運行多個宿主的時候,管理後臺應該請求哪個api,而事實上,咱們應該在管理後臺的配置文件中,把全部集羣的api地址都寫上:
<add key="wechat" value="http://localhost:25250,http://localhost:25260" />
而後,請求的時候,判斷地址是否被監聽,只要被監聽了,post過去就不會有問題.
好比上面這個配置,若是某一天 25250 控制檯掛了,無所謂,25260 不還在麼?請求發送到 25260 就OK了.咱們要作的僅僅是在請求前判斷一下這個地址是否已被監聽就好了,沒被監聽,就換一個.
這個就直接上圖吧!
下面來解釋一下:
首先,4個Job的時間表達式都同樣: 0/30 * * * * ?
我先啓動上面的控制檯,在22:38:00 秒,4個Job都執行完成後,我啓動了下面的控制檯,能夠看到,"負載均衡"是起到了效果的.可是,4個Job在下面的控制檯都各自重複了一次.
並且無論Job的間隔時間是多久,不過失火策略是什麼,無論啞火的忍耐時間是多久,無論我隔多久啓動下面的控制檯,我作了不少實驗,都會重複.並且早晚會重複一次,好比上面的 Job3 .可是隻會重複一次.
這個有點小"坑"...我反覆檢查了個人代碼,感受不是代碼層面的問題.
我不知道這到底緣由是什麼?有沒有哪位前輩知道的?可否告知一下,或者大概多是哪一個方面的緣由?
先到這吧!太晚了,明天繼續寫。
躺在牀上,想起一個問題,判斷api地址是否被監聽不對!
個人集羣是在兩個服務器上.我去......
我傻逼了......媽蛋......睡覺💤