任務排程總結(二)
結合業務
相信以上說了這麼多,大家塵封已久的記憶已經被徹底喚醒。那麼,接下來,我將要結合具體的業務場景,來說說,我們在使用過程中需要注意的點。我將分幾塊進行說明:
一、任務和場景
使用定時任務的場景總結如下:
1、補償機制:
當我們在處理業務的時候,可能會存在處理無結果的情況。而此時不能用快速失敗來處理。
舉例:
支付在呼叫閘道器的時候,閘道器呼叫外部銀行渠道存在很多不可控的因素,如網路超時,或者銀行沒有返回明確結果的情況。這個時候,為了防止資損,我們採用了非同步補償的方式,掉單查詢。

image.png
這裡要說明下,一般這種情況是訊息結合定時查詢來做的。行業內通俗做法。
那麼這裡的查詢就是定時任務來做的。藉助資料庫,進行查詢並更新的操作。
2、特殊業務要求
這裡舉個很通俗的例子:對賬。
無論是第三方支付公司還是銀行,都存在清結算系統,凌晨或者t+1都會進行跑批,對賬。
那麼這個對賬就是定時任務來做的。
所以,這種場景就是業務資料落地後,非同步在某個時間點觸發,進行特殊的業務操作。那麼觸發動作就是定時任務的職責了。
總結下
在聯機操作沒有明確結果的情況下,或者失敗的情況下,需要重試或者補償,保證操作最終成功。那麼定時任務就發揮作用了。
另外,某些特殊的業務,如對賬等功能,就是非同步t+1通過跑批來做的。這個非同步跑批就是定時任務的職責。
最後,我們在寫一些中介軟體的時候,少不了的就是 心跳機制 ,心跳機制也是通過定時任務來做的。通俗做法。
二、任務和鎖
上面,講了通用的場景操作。那麼定時任務在處理的時候,需要注意些什麼。
總結如下幾點:
1、任務緯度:哪些機器跑這個任務。
2、任務緯度:任務失敗了怎麼辦?
3、資料緯度:資料會被多個任務執行麼?
4、資料緯度:資料可以被多個任務一起處理麼?
下面我就來一一解釋下:
老早的土鱉做法:
定時任務跟隨著應用啟動。如何確認資料不被重複執行呢?很簡單,就是隻在叢集的一臺機器上啟動定時任務。
這個時候,怎麼做?
服務啟動的時候jvm引數 -Dxxxxx來注入啟動引數。
這種做法的缺點顯而易見:
服務的啟動指令碼變成了有狀態,或者有差異了。無論對於釋出,還是專業度而言,都很low。
我目前在的這家公司,目前負責的系統,就曾經因為這個指令碼的問題導致資損。
升級做法:
去除指令碼的差異化。那麼這個時候可以通過鎖機制來實現。下面我就來詳細的說說怎麼做:
一、每臺伺服器在啟動的時候,都會去搶佔一把公共鎖。如果搶到,那麼他就執行定時任務。任務就是你的了。
這樣做的 好處 是,任務啟動無狀態,隨機,隨時。
這樣做不好的地方是:依賴鎖機制。如果出現死鎖,或者第三方依賴異常,那麼可能會影響任務,最後影響業務的執行。
當然,有補救的方法,那就是繼續補償嘍。這個裡面的補償,並非繼續再加一個定時任務。
而是, 增加任務的批次和執行結果,配以監控和報警,再加上人工觸發的介面 。是不是perfect了?回答是必須的。如果這種做法再出問題。我揮刀自刎謝罪。哈哈。
所以,我們在定義定時任務的時候,建議做法如下:
1、建任務的批次表
2、建任務的執行日誌表
3、對任務的執行者採用分散式鎖機制。
4、增加報警和監控機制。
5、增加手工觸發入口,以備不時之需。
OK,這裡補充下兩種鎖機制:
1、悲觀鎖
2、樂觀鎖
我們一般會採用悲觀鎖的方式。悲觀鎖,顧名思義,就是爭搶鎖的發起者,很悲觀。認為所有人都會和他競爭。所以搶到了立刻把鑰匙拿走。
通俗的做法是:用共享儲存來做鎖。mysql or zookeeper?
對於mysql來說,做悲觀鎖可能有點麻煩。因為innerdb需要支援悲觀鎖,那麼就要關閉資料庫層面的auto commit。且如果沒有指定具體的主鍵,將是表級鎖。so,我們慎用。所以,一般推薦用zookeeper來做。或者變相的在資料庫層面用樂觀鎖來實現悲觀鎖的方式。
二、分散式任務排程系統
對比以上,任務只能由一臺執行。資料處理的效率並沒有那麼高。所以分散式任務排程系統,很好的解決這個問題。對資料進行分片。將條件注入到sql中去。這裡就不再贅述了。推薦大家看下tbscheduler和elastic-job。
好了,夜深人靜。總結到此。希望對大家對認識和問題的處理有所幫助。
最後,附上我的筆記和我的一些 程式碼示例