對于規模以上的應用來說,調度系統已經是必不可少的組成部分,尤其在基于數據分析的后臺應用大量增長的今天,健壯的調度任務管理已經是非常重要的一環,因此多花些時間來分析研究調度系統的設計對于日常的開發與運維具有比較重要的意義。
調度問題是怎么來的
當你的網站是個簡單的blog,而且并不需要跟外部交互的時候,你大概不需要調度任務,因為此時網站需要處理的任務僅限于 即時交互 , 即用戶想使用一個功能,你就立即給他就是了,如同你在簡書上寫一篇文章,一點保存,這篇文章立即就保存到網站的后臺服務器中去了,這也是互聯網剛出現時候的最早的應用模式。
之后因為網站發展的不錯,用戶多了起來,就發現需要大量處理一些非即時的任務,比如要定時將用戶訪問日志歸檔清理,這個時候一般情況下會在服務器啟動一個定時任務,在每天固定時間發起清理歸檔,又如你想顯示一下網站的訪客流量、發表文章數、評論統計,由于并非每次用戶或者后臺管理員每次需要看的時候都去計算一遍,所以可能又需要啟動另一個任務來去處理這些數據,這樣的任務多了,就需要思考一個問題,哪些任務要先處理,哪些任務要后處理,每個任務要占用多少資源,從而任務調度問題開始出現。
調度什么時候變得復雜
在一個單機的系統,任務并不多的情況下,生活還依然是美好的,利用Linux自帶的定時器或者系統框架提供的定時任務,實現好任務執行器,配置好任務觸發的時間和頻率,然后每天等待它自動觸發,數據生成,任務搞定,似乎調度也沒那么困難。
然而好景不長,伴隨著網站的發展,你發現任務處理的越來越慢,甚至偶爾會有任務超時的情況,原因是每天用戶產生的數據量越來越大,每次任務撈起的數量已經超載,依次執行完每個任務,可能已是最初執行時間的幾倍。
這時稍有點經驗你便會想到,任務沒必要順序執行啊,所以弄個線程池,起多個線程同時來撈數據然后執行,同時配置動態調整每次數據撈取的數量,增大執行頻率,一系列組合拳打出去之后,調度任務又恢復正常,由于增加了并發數,甚至執行的比開始還更快了,就這樣業務高峰便又順利度過了。
調度什么時候變得更加復雜
之前所描述的情況基本上都在單機的情況,網站的QPS(每秒處理的任務數量)基本在500以下,通過一系列的參數調優,串行變并行,任務運行的很平穩。然而當任務變的規模更大,比如十倍于當前,一臺機器已經不能處理所有的任務,這時候需要增加更多的機器,將任務分配到不同的機器上,于是便有了所謂的分布式調度的問題。
分布式是目前稍大型的網站不可避免的問題,這種處理方案有很多好處,比如可以利用廉價的機器,可以(理論上)無限水平拓展,同時也帶來了一系列棘手的問題,機器之間的網絡通信,如何把流量均勻的分布于不同流量,如果有機器宕機了如何處理,每個問題都已經是一個龐大的技術領域。
對于調度的分布式問題,首先要解決的便是如何把任務分發到不同的機器上,這要求調度系統通常要至少分為兩層,第一層決定一共要處理哪些任務并把任務分發到哪些機器上處理,第二層接到任務后具體執行。雖然描述起來很簡單,但是這個處理過程實際上需要大量支撐系統的支持,比如在任務分發時如何判斷哪些機器還活著可以處理任務,這可能需要一個可以感知整個集群運行狀態的配置中心,又比如任務如何分發,是采用消息還是實時服務接口,如果是用消息派發則需要消息系統,如果是實時服務,又需要類似dubbo這樣的分布式服務框架。當系統達到這個復雜度,已經不是將任務撈起并處理這么單純,而是多個關聯系統復雜性的疊加。
分布式調度的難題
雖然分布式調度帶來了復雜度的上升,但是它的水平拓展能力完美的適配了網站的規模發展,直到有一天你看到了類似這個錯誤:
org.springframework.transaction.CannotCreateTransactionException
數據庫連接池已經打滿,由于單個數據庫的連接數是一定的,這意味著數據庫變成了資源瓶頸,這時候給任務處理happy的加機器已經不能提高系統的整體處理能力,這就如同你要運走一群人,給你提供的車輛是無限的,但是汽油確是一定的。當然數據庫也是可以拓展的,但是考慮到數據遷移的復雜性,這幾乎將問題的復雜度提高了一個等級。因此,一個分布式系統的吞吐量,在很大程度上是與數據庫處理能力做權衡的結果。
寫在最后
分布式調度是一個巨大的話題,并且還在隨著實際任務復雜度的提高而快速的更新,上面這些思考與總結也只是浮光掠影,還需要在實際工作中再深入體會與仔細研究。