批處理最佳實踐 - Vlad Mihalcea

banq發表於2020-01-31

大多數應用程式至少具有一個批處理任務,在後臺執行特定的邏輯。編寫批處理作業並不複雜,但是您需要了解一些基本規則,這裡將列舉一些我發現最重要的規則。
從輸入型別的角度來看,處理專案可以透過輪詢資料庫來實現,也可以將資料透過佇列推送到系統中來實現。下面顯示了典型批處理系統的三個主要元件:
  • 輸入元件(透過輪詢或從輸入佇列載入資料專案)
  • 處理器:主要業務處理邏輯元件
  • 輸出元件:輸出結果的輸出通道或儲存位置

分批輪詢
您一次只能檢索一批資料專案。我最近在嘗試檢索所有可能的資料專案進行處理時,排程作業丟擲了OutOfMemoryError。
系統整合測試使用的是少量資料,因此容易透過,但是由於某些部署問題,當計劃排程的作業離線兩天時,由於沒有被觸發使用,因此要處理的資料專案數會大量累積。 ,並且當排程程式重新聯機時,由於資料太大,不適合排程程式的記憶體堆,因此也無法執行。因此,僅設定高的排程頻率還是不夠的。
為避免這種情況,您只需要獲取一批資料,將它們處理消費掉即可,然後您可以重新執行該過程,直到沒有剩餘要處理的東西為止。

編寫執行緒安全的批處理程式
通常,無論您選擇並行執行多少個作業,計劃排程作業都應正確執行。因此,批處理處理器應該是無狀態的,使用本地作業執行的上下文將資料狀態從一個元件傳遞到另一個元件。畢竟,即使是安全的全域性變數也不是那麼安全,因為作業的資料可能在併發執行時混合在一起。

節流
使用佇列時(輸入或在批處理程式中),您應該始終有一個限制策略。如果物品的生產率始終高於被消費的資料量,那麼您將遭受災難。如果排隊的資料專案保留在記憶體中,最終將消耗完記憶體。如果專案儲存在持久佇列中,則佇列空間將用完。因此,您需要一種平衡生產者和消費者的機制。要確保有合適的消費者數量來平衡生產資料。(可透過reactive的背壓)
當佇列大小超過給定閾值時,自動擴充套件消費者就像啟動一個新消費者一樣,是一種合適的自適應策略。當佇列大小低於其他閾值時殺死使用者,可以釋放不必要的空閒執行緒。
create-new-consumer閾值應大於kill-idle閾值,因為如果它們相等,則當佇列大小在閾值大小附近波動時,您將獲得create-kill抖動。

儲存工作結果
只將工作結果儲存在記憶體中是不好的選擇,選擇一個永續性儲存(MongoDB限制的集合)是一個更好的選擇。
如果結果儲存在記憶體中,而不限制一個上限,則批處理處理器最終將耗盡記憶體。重新啟動計劃程式將清除您以前的工作結果,這是非常有價值的處理結果,因為這是您獲得的唯一反饋。

瘋狂的外部服務提供商

for(GeocodeRequest geocodeRequest : batchRequests) {
   mapsService.resolveLocation(geocodeRequest);
}

這段迴圈程式碼會瘋狂呼叫您的地圖服務提供商,這給他們的伺服器帶來了很大壓力。如果這樣批處理請求數很高,那麼您可能會被禁止訪問。
您應該在兩次請求之間新增一個短暫的延遲,但是不要讓您的當前睡眠中斷,而應使用EIP延遲器。

對批處理程式使用EIP樣式程式設計
儘管程式風格的程式設計是大多數程式設計師的預設心態,但許多批處理任務更適合企業整合模式設計。所有上述規則都可以使用EIP工具輕鬆實現,例如:

  • 訊息佇列
  • 投票渠道
  • 變形金剛
  • 分離器/聚合器
  • 延遲器


結論
使用EIP元件可以簡化測試,因為您一次只專注於一項職責。EIP元件透過佇列傳遞的訊息進行通訊,因此將一個同步處理通道更改為排程的執行緒池只是一個配置細節。
有關EIP的更多資訊,請檢視出色的Spring Integration框架。我已經使用了三年了,接受其強迫性以後,您將更喜歡它而不是過程程式設計。

相關:

https://www.jdon.com/53742


 

相關文章