在現(xiàn)代企業(yè)應(yīng)用中汪厨,有很多應(yīng)用和系統(tǒng)需要在生產(chǎn)環(huán)境中使用批處理來執(zhí)行大量的業(yè)務(wù)操作包竹。那么如何實現(xiàn)高效的厉碟、穩(wěn)定的批處理任務(wù)就變得比較重要了躲雅。
本篇將介紹如何構(gòu)建企業(yè)級批處理應(yīng)用鼎姊,以及如何選擇具體的實現(xiàn),包含標(biāo)準(zhǔn)相赁、框架等相寇。然后會著重介紹java平臺下的幾種實現(xiàn),譬如jee7里包含的jsr-352钮科、spring-batch唤衫、easy-batch。
批處理是什么
企業(yè)應(yīng)用中經(jīng)常需要以極高的效率自動地對海量數(shù)據(jù)信息進(jìn)行各種復(fù)雜的業(yè)務(wù)邏輯處理绵脯。執(zhí)行這種操作通常需要根據(jù)時間事件(如月末統(tǒng)計佳励,通知或信件),或是定期處理那些業(yè)務(wù)規(guī)則超級復(fù)雜蛆挫、數(shù)據(jù)量非常龐大的業(yè)務(wù)赃承,也可能是從內(nèi)部/外部系統(tǒng)抓取到的各種數(shù)據(jù)。在這個過程中一般需要格式化悴侵、數(shù)據(jù)校驗瞧剖、以事務(wù)的方式進(jìn)行存儲。企業(yè)中每天處理的事務(wù)量多達(dá)數(shù)十億可免,這類工作就是批處理抓于。
批處理應(yīng)用有以下幾個特點
非交互式、面向批處理浇借、長時間運行
數(shù)據(jù)或計算密集型
順序或并行執(zhí)行
即時捉撮,計劃或按需執(zhí)行
批處理的流程分三個特定的階段
讀數(shù)據(jù),數(shù)據(jù)來源于不同的存儲或消息隊列
業(yè)務(wù)處理逮刨,格式化呕缭、合并、轉(zhuǎn)換等
歸檔結(jié)果數(shù)據(jù)修己,將輸出結(jié)果寫入不同的存儲或消息隊列
批處理設(shè)計原則
將對在線服務(wù)架構(gòu)影響降到最低恢总,盡可能使用公共模塊
盡可能簡化單個批處理應(yīng)用中的邏輯
盡可能在數(shù)據(jù)存儲的地方處理這些數(shù)據(jù)
盡可能少使用io這樣的資源,多使用內(nèi)存
監(jiān)控應(yīng)用程序io睬愤,避免不必要的物理io
在同一個批處理不要做兩次一樣的事
盡可能預(yù)先分配足夠的內(nèi)存
盡可能的加入數(shù)據(jù)校驗以保證數(shù)據(jù)完整性
盡可能早地在模擬生產(chǎn)環(huán)境下使用真實的數(shù)據(jù)量片仿,進(jìn)行計劃和執(zhí)行壓力測試
批處理包括
常規(guī)批處理
并發(fā)批處理
并行批處理
分區(qū)批處理
批處理健壯性需求
監(jiān)控,可以監(jiān)控執(zhí)行中的狀態(tài)
跳過尤辱,可以在任務(wù)執(zhí)行遇到異成巴悖或者故障時候厢岂,進(jìn)行選擇性的跳過
任務(wù)重啟,任務(wù)如遇到卡在某一步的時候阳距,可以試著重啟的方式繼續(xù)從故障點開始往后執(zhí)行
重試塔粒,在遇到某個異常的時候,可嘗試重試來完成任務(wù)要求
技術(shù)選型
上文對批處理有了一些了解筐摘,那么我們該如何去實現(xiàn)呢卒茬?選擇自己開發(fā)這樣一套涵蓋上述方方面面的框架,還是選擇開源的框架咖熟、或者也有的Java EE支持部分圃酵?既然要選擇,就應(yīng)該明曉哪些點是你所關(guān)注的部分馍管,以下介紹一些點以備選擇參考郭赐。
可測試性,是否可以更方面對每個環(huán)節(jié)進(jìn)行測試
組件化确沸,是否具備一套完備的組件支撐
可觀察性捌锭,是否方便監(jiān)控各種狀態(tài)
社區(qū)化,是否有良好的社區(qū)支持张惹,產(chǎn)品更迭速度舀锨,方便遇到問題能夠有較好的支持
可伸縮性岭洲,比如Multi-threaded宛逗、Partitioning、Parallel 盾剩、Remote 的支持
可配置性雷激,是否支持良好的xml配置、以及java annotation等
可擴展性告私,比如對啟動屎暇、重啟任務(wù)有更多的擴展接口支持
幾種實現(xiàn)
JSR-352、Spring Batch驻粟,Easy Batch根悼、JBatch IBM (Glassfish, JEUS)、JBeret (Wildfly)都有一套對batch processing的實現(xiàn)蜀撑,本文僅對Spring Batch的架構(gòu)挤巡、決策器、伸縮性(批處理選項)酷麦、健壯性等方面進(jìn)行介紹矿卑。
Spring Batch
SpringSource與Accenture合作開發(fā)了Spring Batch
Spring Batch借鑒了JCL(Job Control Language)和COBOL的語言特性
Spring Batch一款優(yōu)秀的、開源的大數(shù)據(jù)量并?處理框架
Spring Batch可以構(gòu)建出輕量級的健壯的并?處理應(yīng)?沃饶,?持事務(wù)母廷、并發(fā)轻黑、流程、監(jiān)控琴昆、縱向和橫向擴展氓鄙,提供統(tǒng)?的接口管理和任務(wù)管理
Spring Batch 3.x對jsr-352有支持。
分層架構(gòu)
基礎(chǔ)設(shè)施層玖详,主要提供策略方面的支持,比如輸入勤讽、輸出蟋座、事務(wù)、重試等
批處理執(zhí)行環(huán)境脚牍,主要提供批處理需要各種領(lǐng)域?qū)ο笙蛲危热鏙ob、Step等
批處理核心組件诸狭,主要提供給應(yīng)用層需要的api支持
業(yè)務(wù)應(yīng)用層券膀,開發(fā)各式的批處理業(yè)務(wù)
上下文
外部控制器調(diào)用JobLauncher啟動一個Job,Job調(diào)用自己的Step去實現(xiàn)對數(shù)據(jù)的操作驯遇,Step處理完成后芹彬,再將處理結(jié)果一步步返回給上一層,這就是Batch處理實現(xiàn)的一個簡單流程叉庐。
執(zhí)行過程
說明如下:
每個Batch都會包含一個Job舒帮。Job就像一個容器,容器里裝了若干個Step陡叠,Batch中實際干活的也就是這些Step玩郊,至于Step干什么活,無外乎讀取數(shù)據(jù)枉阵,處理數(shù)據(jù)译红,然后將這些數(shù)據(jù)存儲起來(ItemReader用來讀取數(shù)據(jù),ItemProcessor用來處理數(shù)據(jù)兴溜,ItemWriter用來寫數(shù)據(jù)) 侦厚。JobLauncher用來啟動Job,JobRepository是上述處理提供的一種持久化機制(它為JobLauncher拙徽,Job刨沦,和Step實例提供CRUD操作)。
簡單實現(xiàn)樣例:
commit-interval="1">
上面是一個最基本的配置斋攀,包含了批處理流程中的三個階段已卷,其處理流程如下圖:
從特定的數(shù)據(jù)源取出數(shù)據(jù)的時候,read()操作每次只讀取一條記錄淳蔼,之后將讀取的這條數(shù)據(jù)傳遞給processor(item)處理侧蘸,框架將重復(fù)做這兩步操作裁眯,直到讀取記錄的件數(shù)達(dá)到batch配置信息中”commin-interval”設(shè)定值的時候,就會調(diào)用一次write操作讳癌。然后再重復(fù)上圖的處理穿稳,直到處理完所有的數(shù)據(jù)。當(dāng)這個Step的工作完成以后晌坤,或是跳到其他Step逢艘,或是結(jié)束處理。
詳細(xì)的處理流程示意圖:
批量提交次數(shù)為1骤菠,commit-interval="1"它改。
下面介紹具體組件。
領(lǐng)域?qū)ο?/b>
Job
Job -- 由?組Step構(gòu)成,完成Batch數(shù)據(jù)操作的整個過程
Job Instance -- 特定的運行時Job實例,由Job launcher運行
Job Execution -- 某個Job實例的執(zhí)?信息,包括執(zhí)?時間商乎、狀態(tài)央拖、退出代碼等
Job實例和執(zhí)?數(shù)據(jù)、參數(shù)等元數(shù)據(jù)信息都由Job repository進(jìn)?持久化
啟動Job, jobLauncher.run(demoJob, jobParameterBulider.toJobParameters());
JobParameters
JobParameters鹉戚,就是Job運行時的參數(shù)鲜戒。可以表示不同的JobInstance和給job傳參數(shù)抹凳。
在啟動job時遏餐,設(shè)置參數(shù)的key/value即可。
jobLauncher.run(job, new JobParametersBuilder()
.addString("inputFilePath", "/tmp/index.txt").toJobParameters()
如代碼所示赢底,參數(shù)inputFilePath傳給Job了失都,在Job中如果需要使用參數(shù)信息,可以使用Spring注入的方式傳給不同的使用對象颖系。
需要設(shè)置Bean的scope屬性為step嗅剖。這是Spring Batch的一個后綁定技術(shù),就是在生成Step的時候嘁扼,才去創(chuàng)建bean,因為這個時候JobParameter才傳過來黔攒。如果加載配置信息的時候就創(chuàng)建bean趁啸,這個時候JobParameter的值還沒有產(chǎn)生,會拋出異常督惰。
JobParametersIncrementer
同一個Job在batch啟動后被多次調(diào)用的時候不傅,需要創(chuàng)建一個新實例。JobParametersIncrementer接口提供了getNext方法赏胚,可以為parameters添加一個自增的值访娶,以區(qū)分不同的Job實例。RunIdIncrementer就是Spring Batch框架提供一個實現(xiàn)類觉阅。使用方法如下:
class="org.springframework.batch.core.launch.support.RunIdIncrementer"/>
RunIdIncrementer的getNext方法實現(xiàn)如下:
public JobParameters getNext(JobParameters parameters) {
if (parameters == null) {
parameters = new JobParameters();
}
long id = parameters.getLong(key, 0L) + 1;
return new JobParametersBuilder(parameters).addLong(key, id).toJobParameters();
}
由代碼可以看出崖疤,通過id值加一的方式來保證了每次創(chuàng)建的jobInstance的唯一性秘车。
Step
Step是Job的一個執(zhí)行階段
Step通過tasklet和chunk元素控制數(shù)據(jù)的處理策略
一組Step可以順序執(zhí)行,也可以根據(jù)條件分段執(zhí)行
Step的執(zhí)?數(shù)據(jù)同樣由Job repository進(jìn)行持久化
DataSource
File
XML
Database
Message(JMS、AMQP)
決策器
前文的樣例部分介紹了一步步執(zhí)行的順序Job劫哼,那么如何按Step執(zhí)行結(jié)果來選擇后續(xù)的Step呢叮趴?這里介紹的Decision就是一種按分支來執(zhí)行的Job。如下圖所示权烧,可以看到返回為SKIPPER時候跳到generateReport這個Step眯亦,其他的都直接跳到clean這個Step。
配置如下:
監(jiān)控
Spring Batch提供了4種監(jiān)控?式:
直接查看Job repository的數(shù)據(jù)庫信息般码,所有的Batch元數(shù)據(jù)都會持久化到數(shù)據(jù)庫中
使用Spring Batch提供的API自?構(gòu)建監(jiān)控數(shù)據(jù)
使用Spring Batch Admin妻率,通過web控制臺監(jiān)控和操作Job
使用JMX的方式
spring batch admin提供的控制臺:
健壯性
主要包含重啟、跳過板祝、重試等舌涨。
重啟(restart)
在某個job執(zhí)行發(fā)生異常時,可能執(zhí)行完成了某些步驟扔字,期望重啟相同參數(shù)的該次job囊嘉,就可以用restart參數(shù)來進(jìn)行設(shè)置。
可配置參數(shù)allow-start-if-complete來設(shè)置開啟重啟革为,start-limit設(shè)置重啟限制次數(shù)
重啟相同job參數(shù)的job launch
配置如下:
跳過(skip)
再發(fā)生非致命異常時候扭粱,比如某些值不符合格式要求,程序檢查發(fā)生異常時震檩。這樣的情況一般選擇設(shè)置跳過即可琢蛤。
配置如下:
class="org.springframework.batch.item.file.FlatFileParseException" />
重試(retry)
發(fā)?瞬態(tài)異常,當(dāng)發(fā)?瞬態(tài)失敗的時候進(jìn)行重試(例如遇到記錄鎖的情況)抛虏,一般在Chunk的Step和應(yīng)用程序中進(jìn)行配置或處理博其。
配置如下:
commit-interval="5" retry-limit="3" skip-limit="3" >
伸縮性
Spring Batch支持Multi-threaded、Partitioning迂猴、Parallel 慕淡、Remote四種伸縮方式。
Multi-threaded方式
單線程執(zhí)行情況
前文已有部分介紹沸毁,當(dāng)批量提交次數(shù)為1的情況峰髓。批量提交次數(shù)為多個, commit-interval="3"見下圖息尺。
多線程方式
多個線程并行執(zhí)行chunk携兵。
配置如下:
commit-interval="1000">
線程池的配置
需要注意的是,在多線程Step中搂誉,需要確保Reader徐紧、Processor和Writer是線程安全的,否則容易出現(xiàn)并發(fā)問題。Spring Batch提供的大部分組件都是非線程安全的并级,他們都保存有部分狀態(tài)信息拂檩,主要是為了支持任務(wù)重啟。
因此死遭,使用多線程Step的核心任務(wù)是實現(xiàn)無狀態(tài)化广恢,例如不保存當(dāng)前讀取的item的cursor,而是同item的flag字段來區(qū)分item是否被處理過呀潭,已經(jīng)被處理過的下次重啟的時候钉迷,直接被過濾掉。多線程Step實現(xiàn)的是單個Step的多線程化钠署。
Partitioning方式
分區(qū)主要包含:
數(shù)據(jù)分區(qū)
分區(qū)處理
方式基本上包含:
Local Partitioning
Remote Partitioning
Spring Batch提供了一個同一臺機器上的Handler實現(xiàn)糠聪,在同一機器上創(chuàng)建多個Step Execution。
Local Partitioning
配置如下:
processor="itemProcessor" commit-interval="1" />
Remote Partitioning
處理遠(yuǎn)程分塊來執(zhí)行的方式外谐鼎,在遠(yuǎn)程分塊的同時還可以加上分區(qū)來實現(xiàn)遠(yuǎn)程分塊分區(qū)的實現(xiàn)舰蟆。
詳細(xì)的實現(xiàn)以及配置就不做說明了,主要是用到spring integration來做消息集成狸棍。
Remote
使用遠(yuǎn)程分塊的Step被拆分成多個進(jìn)程進(jìn)行處理身害,多個進(jìn)程間通過中間件實現(xiàn)通信。
下面是一幅模型示意圖:
Master組件是單個進(jìn)程草戈,從屬組件(Slaves)一般是多個遠(yuǎn)程進(jìn)程塌鸯。如果Master進(jìn)程不是瓶頸的話,那么這種模式的效果幾乎是最好的唐片,因此應(yīng)該在處理數(shù)據(jù)比讀取數(shù)據(jù)消耗更多時間的情況下使用(實際應(yīng)用中常常是這種情形)丙猬。
Parallel
需要并行的程序邏輯可以劃分為不同的職責(zé),并分配給各個獨立的step费韭,那么就可以在單個進(jìn)程中并行執(zhí)行茧球。并行Step執(zhí)行很容易配置和使用,如下圖所示:
配置如下:
commit-interval="1000">
commit-interval="1000">
四種方式比較
前面對四種方式的實現(xiàn)方式和圖示已做了簡單介紹星持,下面我們再次看看四種方式的應(yīng)用上的差異抢埋,做一個比較。
寫在最后
以上為筆者在使用過程中的心得體會钉汗。目前spring batch 3.x 已涵蓋了批處理的多方面的內(nèi)容羹令,包含對jsr-352也有較好的支持、也提供了豐富的接口损痰,易于擴展,且上手相對容易酒来,可是對annotation的配置方式支持不夠全卢未,期望后續(xù)的版本能夠有改進(jìn)。
參考:
選擇正確的批處理實現(xiàn)
Horizontal and Vertical Scaling Strategies for Batch Applications
Spring Batch Behind the Scenes
本文作者:楊濤(點融黑幫),目前在點融網(wǎng)架構(gòu)組從事應(yīng)用架構(gòu)相關(guān)工作病毡。對微服務(wù)和docker技術(shù)有較多的實戰(zhàn)經(jīng)驗审孽。