系統架構設計之-任務調度系統的設計

来源:https://www.cnblogs.com/mylanqiu/archive/2018/09/30/9727910.html
-Advertisement-
Play Games

實習生張大胖這是個代碼寫得很爛的電商系統,只要運行一段時間,伺服器就會出現Out Of Memory。別人都忙得四腳朝天,於是實習生張大胖被抓了壯丁去研究為什麼會出現OOM。剛入行的張大胖技術水平一般,“裝模作樣”地看代碼,研究日誌,請教老員工,一個星期過去了,還是一無所獲。周一例行的項目會議上, ...


實習生張大胖

這是個代碼寫得很爛的電商系統,只要運行一段時間,伺服器就會出現Out Of Memory。

別人都忙得四腳朝天,於是實習生張大胖被抓了壯丁去研究為什麼會出現OOM。

剛入行的張大胖技術水平一般,“裝模作樣”地看代碼,研究日誌,請教老員工,一個星期過去了,還是一無所獲。

周一例行的項目會議上, 大家似乎要看張大胖的笑話了,沒想到他卻提了一個歪招:“這個OOM問題非常複雜,一時半會兒也解決不了,要不我們定時重啟伺服器怎麼樣?”

一臉嚴肅的項目經理老梁點點頭:“以目前的情況看,也只能如此了。但是不能讓服務中斷,這樣吧,公司有兩台伺服器,一臺在凌晨1點重啟, 另外一臺在凌晨2點重啟。”

得到了領導的首肯,張大胖趕緊行動,周末他其實已經做了準備,研究了Linux上的crontab,它的格式是這樣樣子:

每天凌晨一點重啟系統,可以這麼寫:
0 1 * * *  restart.sh
(註:這裡只是個簡單的例子, 實際上crontab及其靈活)

這個OOM的問題被張大胖靈機一動給解決了,或者說,被臨時隱藏了。

crontab達人的煩惱

大家知道張大胖擅長crontab, 都把一些定時的任務扔給他去做: 什麼定時統計報表,定時同步數據,定時刪除表中的無效訂單...... 等等。

張大胖整天面對的就是crontab和腳本,都快要吐了。

不僅如此,同事們還經常提出一些“變態”的需求:
“大胖,那個定時任務運行得怎麼樣了?”
“大胖,我想把那個定時任務給停掉。”
“大胖,那個定時任務今晚別運行啊!”
“......”

張大胖真是煩死了,他心想,要是提供個界面讓大家使用就好了, 可是crontab似乎並不支持。

要不自己開發一個?

有一次張大胖偶然發現了JDK中的Timer類,似乎也是做這些定時任務的, 不由地眼前一亮,但是仔細研究以後就發現,JDK的Timer還是太簡單了,做點簡單的定時任務還行, 對於複雜的情況,尤其是複雜的時間策略,還是力不從心。

另起爐竈

看來自己需要從頭設計了,張大胖想到了一篇文章《一個著名的日誌系統是怎麼設計出來的?》, 小張用“正交”的原則設計出了Logger, Appender, Formatter這些類。

我也可以使用同樣的原則啊,小張能行,我憑什麼不行?

說乾就乾,先想想需求,非常簡單,不就是定時地執行任務嘛!

“任務”應該是正交中的一個“維度”,我可以抽象出一個介面叫做Task , 嗯,還是叫做Job吧。

對使用者來說,他需要提供一個實現類出來,在實現類中描述要做什麼事情,比如:生成報表,複製數據......

“定時”該怎麼處理? 定時,定時觸發,乾脆叫做Trigger吧。

這個Trigger 可以指定什麼時間開始,時間間隔,運行多少次, 能覆蓋大部分需求了。

可是張大胖轉念一想,如果有人要求類似日曆的重覆間隔該怎麼處理? 比如每月的第一天運行,或者每周的最後一天運行,該怎麼辦?  crontab特別適合描述這種情況,對,可以搞一個類似於crontab的Trigger。

看來Trigger最好也是個介面,我來提供幾個預設的實現,比如SimpleTrigger,CronTrigger,用戶還可以擴展,這樣就靈活了。

Job和Trigger也是正交的關係, 兩者可以互不影響,可以獨立擴展,真是不錯, 張大胖不僅得意起來,這設計也很簡單嘛!

但是怎麼把這兩個家伙結合起來?

必須得有個“大管家”才行,這個大管家應該可以接受Job, 然後按照各種Trigger去運行,嗯,叫做調度器Scheduler應該不錯。

張大胖畫了個草圖,來展示三者之間的關係:

設計得差不多了,可以進入開發階段了, 因為是自己要寫一個類似於框架的東西,讓別人去使用,張大胖開發起來非常有激情,即使是利用晚上和周末的時間來寫代碼,也是像打了雞血一樣,根本不覺得累。

一個月過去了,第一版新鮮出爐。

這個版本不僅有核心的API像Job, Trigger, Scheduler ,張大胖還專門開發了一個界面,用來展示定時任務的進展,例如什麼時間運行,運行了幾次,失敗了幾次......等等。

張大胖把它叫做“大胖定時任務調度系統”。

持久化

他興奮地拿去讓項目經理老梁看, 可是老梁並不感冒,面無表情地說:“你這個小軟體有啥用啊。”

張大胖被潑了一盆冷水,依然熱情滿滿地推銷:“用了我的這個定時調度系統,任何人都可以輕鬆地啟動,停止任務, 咱們項目中所有的定時任務一目瞭然。 大家就不用找我來手工調整了。”

老梁開玩笑地說:“奧,那你的實習工作就可以結束了,哈哈。”

正巧CTO Bill經過,他饒有興趣地看了一會,提了一個問題:“假設你這個大胖調度系統在運行的時候,機器突然間Down掉了,怎麼處理?”

張大胖一臉懵逼:“什麼怎麼處理,重啟機器唄。”

Bill 說: “之前的任務還能接著運行嗎,比如說一個任務需要運行100次,在機器down掉之前運行了90次,重啟後能不能從第91次運行?”

張大胖有點發窘,不好意思地撓撓頭:“這一點我還真沒考慮到,我現在都是在記憶體中記錄運行的情況,看來得做持久化了。”

Bill 聽到持久化這個詞,知道張大胖已經Get到了,他說,你把這個持久化實現了,到時候直接向我彙報。

得到了CTO的賞識,張大胖不敢怠慢,趕緊進行新的設計, 他抽象了一個叫做JobStore的介面,表示Job的存儲,像什麼Job,Trigger, Job運行情況都存儲在其中。 

下麵有兩個實現,分別對應記憶體存儲和資料庫存儲。

雖然SQL是標準的,但是不同的資料庫還是有細微的差異, 張大胖覺得得把這些差異給封裝起來, 他又提取了一個介面叫做DriverDelegate, 屏蔽了資料庫細節,讓DbJobStore使用。

他還提供了一個預設的實現StdJDBCDelegate,如果那些資料庫還有獨特的實現,那就寫個子類就行了。

高可用

“大胖定時任務調度系統 2.0” 開發完成以後,張大胖仔細地想了一遍,似乎沒有什麼漏洞了,決定正式向CTO Bill去彙報。

Bill 親切地詢問了張大胖加班加點設計和開發的情況,對他這種不計較個人得失,一心一意為公司謀福利的精神表示了高度的贊賞。

張大胖受寵若驚。

Bill話鋒一轉:“我們的系統最近用戶越來越多,老闆特別提出了高可用的需求,系統的各個組件也得達到高可用!”

“高可用? 拿我的定時調度系統來說,就是說可以部署在多個機器上,一個down掉了,其他的還可以運行,對吧?” 張大胖一點就透。

Bill 贊許地點點頭:“你想好怎麼去實現了嗎?”

“很簡單啊,把定時調度系統部署到多個機器上,形成幾個備份就行了!”

張大胖還在白板上畫了這麼一個圖:

“那同一個時刻,有多少個Scheduler 在運行?”  Bill 終於拋出了重磅炸彈。

張大胖現在明白Bill的疑問了了,三個實例都在運行,那一個Job就有可能運行多次,這肯定是不行的!

他說道:“要不讓三個實例A,B,C都去訪問同一個資料庫吧!”

Bill說:“那三個實例訪問同一份數據,肯定會出現衝突,互相覆蓋,那就亂套了!”

其實,實例A,實例B,實例C組成一個類似集群的東西,但是同一時刻,一個Job只能在一個實例上運行。

比如Job X 從凌晨1點開始,每隔1小時運行一次,那1:00 的時候Job X可能在實例A上運行, 2:00的時候可能在實例B上運行, 3:00的時候可能在實例C上運行。

也就是說,這三個實例部分地實現了負載均衡。

張大胖說:“這可就難辦了。難道讓這三個實例A,B,C之間互相通信?”

Bill說道:“那樣有點麻煩,就變成一個分散式系統下的通信問題了,我們要不用這個資料庫做點文章? 反正這個資料庫已經存了Job的信息,Trigger的信息,我們就多加一個表吧,就叫LOCKS,這個表裡邊每一行記錄都可以當做一個‘鎖’來用。”

張大胖表示不太明白。

“很簡單,就是資料庫的‘行’鎖嘛, 比如SELECT * FROM LOCKS where LOCK_NAME='TRIGGER' FOR UPDATE ,這就把那一行記錄給鎖住了, 別的事務只能等待當前事務commit以後才能訪問。”

張大胖還是不太明白。

“比如,伺服器A的實例A在一個事務中先執行了上面SQL, 就把那一行給鎖住了,當伺服器B的實例B也去執行同樣的SQL的時候, 只能等待,對吧? 這不就相當於實例A獲得了鎖嗎?”

“原來如此,以後任何一個調度器實例想要獲取Job的運行時間,設置Job的下一次運行時間的時候,都必須先獲得這個鎖。這樣這些分散式的調度器就不會衝突了,只會運行一個特定時間的Job。 我這就去做個詳細設計,再來彙報。”

開源

兩個月後,“大胖定時任務調度系統 3.0” 開發完畢,在Bill的大力支持和推動下,成功地應用在了公司的項目中。

靈活的設計和擴展性,加上持久化,集群等強大的功能, 系統受到了大家的歡迎。

考慮到很多公司都會有類似的需求,Bill決定把系統開源, 只是“大胖定時任務調度系統”這個名字有點俗,還有點長,Bill把它改名為“Quartz”。

Quartz從此流行開來。

本文轉載自:http://www.dalbll.com/Group/Topic/ArchitecturedDesign/5246


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • What is Promise? Promise是一個構造函數,接受一個參數(Function),並且該參數接受兩個參數resolve和reject(分別表示非同步操作執行成功後的回調函數、執行失敗後的回調函數) 運行代碼,2秒後輸出“執行完成”。註意,這裡只是new了一個對象,並沒有調用它,所以我們 ...
  • 騰訊防水牆(滑動驗證碼)的簡單使用 https://007.qq.com ...
  • 騰訊地圖點擊地圖創建錨點(且只創建一個)同事創建錨點提示。 qq.maps.event.addListener(marker, 'click', function() { 44 info.open(); 45 info.setContent('單擊標記'); 47... ...
  • 今天分享一下快速使用jQuery+zepto.js的技巧,需要的記得收藏 1.jQuery的引入:本地下載jQuery(後面簡稱jq)的源文件,開發版本使用非min版,線上使用min版,zepto.js類似,同樣的一些基於jq的插件也是如此用法,如果使用requreJs也就是模塊化載入註意插件的依賴 ...
  • 叨叨一會CommonJS Common這個英文單詞的意思,相信大家都認識,我記得有一個片語common knowledge是常識的意思,那麼CommonJS是不是也是類似於常識性的,大家都理解的意思呢?很明顯不是,這個常識一點都不常識。我最初認為commonJS是一個開源的JS庫,就是那種非常方便用 ...
  • webpack的打包依賴於它的一個重要配置文件webpack.config.js,在這個配置文件中就可以指定所有在源代碼編譯過程中的工作了,就一個配置就可以與冗長的Gruntfile或者Gulpfile說再見了。 一個完整的工程項目中的webpack的配置遠遠沒有這麼簡單,隨著工程的構建要求的增加, ...
  • 最近一個月,恍若隔世,天天加班,昨晚終於發版了,今天才喘一口氣。有時候,即便你工作效率再怎麼高,擼碼再怎麼快也無可避免的會加班。不信的話,可以先給你定一個交付時間,然後不斷的給你加需求,就讓你一個人做,偶爾再給你來幾下變更,看你怎麼死?本來做項目,需求、資源、時間就是一個鐵三角,一旦你強行要打破這個 ...
  • 我們在上一篇說到如何把 Vue 實例中的數據顯示到視圖中,就會需要用到我們的模版,我們只是簡單的使用了一些,模版其實還有很多其他的特性。今天我們就來看看模版的其他特性。 模版語法 Vue 中的模版是基於 HTML 的模版語法,所有的 Vue 模版都是合法的 HTML ,所以能被遵循規範的瀏覽器和 H ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...