Quartz.NET 3.0.7 + MySql 動態調度做業+動態切換版本+多做業引用同一程序集不一樣版本+持久化+集羣(二)

 

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;
View Code

那麼這些數據如何讓調度服務知道呢?(因爲調研 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次等等

 對這個界面作一個簡單的說明:

  • 從"編號"到"程序集",這些字段的值來自做業表 jobInfo.
  • "狀態","開始時間","上次執行","下次執行"4個字段來自官方的 qrtz_triggers 表.
  • 頁面暫時不是實時的,要看最新狀態須要F5刷新.

對於"調度器名稱"字段須要特別說明.

整個框架開發到一半的時候,來了個新需求:

要同時開多個調度服務(控制檯程序)調度不一樣的任務,可是用同一張數據庫表,同一個管理後臺來管理.

好比如今已經啓了一個控制檯程序了,管理了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地址是否被監聽不對!

個人集羣是在兩個服務器上.我去......

我傻逼了......媽蛋......睡覺💤

相關文章
相關標籤/搜索