天天看點

Spring Batch在大型企業中的最佳實踐

在大型企業中,由于業務複雜、資料量大、資料格式不同、資料互動格式繁雜,并非所有的操作都能通過互動界面進行處理。而有一些操作需要定期讀取大批量的資料,然後進行一系列的後續處理。這樣的過程就是“批處理”。

Spring Batch在大型企業中的最佳實踐

批處理應用通常有以下特點:

資料量大,從數萬到數百萬甚至上億不等;

整個過程全部自動化,并預留一定接口進行自定義配置;

這樣的應用通常是周期性運作,比如按日、周、月運作;

對資料處理的準确性要求高,并且需要容錯機制、復原機制、完善的日志監控等。

<a></a>

spring batch是一個輕量級的全面的批處理架構,它專為大型企業而設計,幫助開發健壯的批處理應用。spring

batch為處理大批量資料提供了很多必要的可重用功能,比如日志追蹤、事務管理、job執行統計、重新開機job和資源管理等。同時它也提供了優化和分片技術用于實作高性能的批處理任務。

它的核心功能包括:

事務管理

基于塊的處理過程

聲明式的輸入/輸出操作

啟動、終止、重新開機任務

重試/跳過任務

基于web的管理者接口

筆者所在的部門屬于國外某大型金融公司的crm部門,在日常工作中我們經常需要開發一些批處理應用,對spring batch有着豐富的使用經驗。近段時間筆者特意總結了這些經驗。

Spring Batch在大型企業中的最佳實踐

在使用spring batch時推薦使用最新的spring batch 3.0版本。相比spring batch2.2,它做了以下方面的提升:

支援jsr-352标準

支援spring4以及java8

增強了spring batch integration的功能

支援jobscope

支援sqlite

支援spring4和java8是一個重大的提升。這樣就可以使用spring4引入的spring boot元件,進而開發效率方面有了一個質的飛躍。引入spring-batch架構隻需要在build.gradle中加入一行代碼即可:

而增強spring batch integration的功能後,我們就可以很友善的和spring家族的其他元件內建,還可以以多種方式來調用job,也支援遠端分區操作以及遠端塊處理。

而支援jobscope後我們可以随時為對象注入目前job執行個體的上下文資訊。隻要我們指定bean的scope為job scope,那麼就可以随時使用jobparameters和jobexecutioncontext等資訊。

之前我們在配置job和step的時候都習慣用xml的配置方式,但是随着時間的推移發現問題頗多。

xml檔案數急劇膨脹,配置塊長且複雜,可讀性很差;

xml檔案缺少文法檢查,有些低級錯誤隻有在運作內建測試的時候才能發現;

在xml檔案中進行代碼跳轉時ide的支援力度不夠;

我們漸漸發現使用純java類的配置方式更靈活,它是類型安全的,而且ide的支援更好。在建構job或step時采用的流式文法相比xml更加簡潔易懂。

在這個例子中可以很清楚的看到該step的配置,比如reader/processor/writer元件,以及配置了哪些listener等。

spring

batch在運作時需要資料庫支援,因為它需要在資料庫中建立一套schema來存儲job和step運作的統計資訊。而在本地內建測試中我們可以借助spring

batch提供的記憶體repository來存儲spring

batch的任務執行資訊,這樣既避免了在本地配置一個資料庫,又可以加快job的執行。先為job的配置類添加擴充類:defaultbatchconfigurer。

我們在build.gradle中加入對hsqldb的依賴:

然後在測試類中添加對datasource的配置。

并且在applicaton.properties配置中添加初始化database的配置:

spring batch在配置step時采用的是基于chunk的機制。即每次讀取一條資料,再處理一條資料,累積到一定數量後再一次性交給writer進行寫入操作。這樣可以最大化的優化寫入效率,整個事務也是基于chunk來進行。

當我們在需要将資料寫入到檔案、資料庫中之類的操作時可以适當設定chunk的值以滿足寫入效率最大化。但有些場景下我們的寫入操作其實是調用一個web

service或者将消息發送到某個消息隊列中,那麼這些場景下我們就需要設定chunk的值為1,這樣既可以及時的處理寫入,也不會由于整個chunk中發生異常後,在重試時出現重複調用服務或者重複發送消息的情況。

spring batch提供了大量的listener來對job的各個執行環節進行全面的監控。

在job層面spring

batch提供了jobexecutionlistener接口,其支援在job開始或結束時進行一些額外處理。在step層面spring

batch提供了stepexecutionlistener,chunklistener,itemreadlistener,itemprocesslistener,itemwritelistener,skiplistener等接口,同時對retry和skip操作也提供了retrylistener及skiplistener。

通常我們會為每個job都實作一個jobexecutionlistener,在afterjob操作中我們輸出job的執行資訊,包括執行時間、job參數、退出代碼、執行的step以及每個step的詳細資訊。這樣無論是開發、測試還是運維人員都對整個job的執行情況了如指掌。

如果某個step會發生skip的操作,我們也會為其實作一個skiplistener,并在其中記錄skip的資料條目,用于下一步的處理。

實作listener有兩種方式,一種是繼承自相應的接口,比如繼承jobexecutionlistener接口,另一種是使用annoation(注解)的方式。經過實踐我們認為使用注解的方式更好一些,因為使用接口你需要實作接口的所有方法,而使用注解則隻需要對相應的方法添加annoation即可。

下面的這個類采用了繼承接口的方式,我們看到其實我們隻用到了第一個方法,第二個和第三個都沒有用到。但是我們必須提供一個空的實作。

而使用annoation的方式可以簡寫為:

在處理百萬級的資料過程過程中難免會出現異常。如果一旦出現異常而導緻整個批處理工作終止的話那麼會導緻後續的資料無法被處理。spring

batch内置了retry(重試)和skip(跳過)機制幫助我們輕松處理各種異常。我們需要将異常分為三種類型。第一種是需要進行retry的異常,它們的特點是該異常可能會随着時間推移而消失,比如資料庫目前有鎖無法寫入、web服務目前不可用、web服務滿載等。是以對它們适合配置retry機制。第二種是需要skip的異常,比如解析檔案的某條資料出現異常等,因為對這些異常即使執行retry每次的結果也都是相同,但又不想由于某條資料出錯而停止對後續資料的處理。第三種異常是需要讓整個job立刻失敗的異常,比如如果出現了outofmemory的異常,那麼需要整個job立刻終止運作。

一般來說需要retry的異常也要配置skip選項,進而保證後續的資料能夠被繼續處理。我們也可以配置skiplimit選項保證當skip的資料條目達到一定數量後及時終止整個job。

有時候我們需要在每次retry中間隔做一些操作,比如延長retry時間,恢複操作現場等,spring batch提供了backoffpolicy來達到目的。下面是一個配置了retry機制、skip機制以及backoffpolicy的step示例。

在job執行過程中不一定都是順序執行的,我們經常需要根據某個job的輸出資料或執行結果來決定下一步的走向。以前我們會把一些判斷放置在下遊step中進行,這樣可能會導緻有些step實際運作了,但其實并沒有做任何事情。比如一個step執行過程中會将失敗的資料條目記錄到一個報告中,而下一個step會判斷有沒有生成報告,如果生成了報告則将該報告發送給指定聯系人,如果沒有則不做任何事情。這種情況下可以通過decider機制來實作job的執行流程。在spring

batch 3.0中decider已經從step中獨立出來,和step處于同一級别。

而在job配置中可以這樣來使用decider。這樣整個job的執行流程會更加清晰易懂。

批處理工作處理的資料量大,而執行視窗一般又要求比較小。是以必須要通過多種方式來加速job的執行。一般我們有四種方式來實作:

在單個step中多線程執行任務

并行執行不同的step

并行執行同一個step

遠端執行chunk任務

上述示例中的tasklet需要實作taskexecutor,spring batch提供了一個簡單的多線程taskexecutor供我們使用:simpleasynctaskexecutor。

并行執行不同的step在spring batch中很容易實作,以下是一個示例:

在這個示例中我們先執行step1,然後并行執行flow1和flow2,最後再執行step3。

spring batch提供了partitionstep來實作對同一個step在多個程序中實作并行處理。通過partitonstep再配合partitionhandler可以将一個step擴充到多個slave上實作并行運作。

遠端執行chunk任務則是将某個step的processer操作分割到多個程序中,多個程序通過一些中間件進行通訊(比如采用消息的方式)。這種方式适合于processer是瓶頸而reader和writer不是瓶頸的場景。

spring batch對批處理場景進行了合理的抽象,封裝了大量的實用功能,使用它來開發批處理應用可以達到事半功倍的效果。在使用的過程中我們仍需要堅持總結一些最佳實踐,進而能夠傳遞高品質的可維護的批處理應用,滿足企業級應用的苛刻要求。

作者:無敵北瓜

來源:51cto