# 什么是 Spring Batch
<br/>
### 介紹
Spring Batch 作為 Spring 的子項(xiàng)目慢宗,是一款基于 Spring 的企業(yè)批處理框架。通過(guò)它可以構(gòu)建出健壯的企業(yè)批處理應(yīng)用。Spring Batch 不僅提供了統(tǒng)一的讀寫接口力麸、豐富的任務(wù)處理方式拐辽、靈活的事務(wù)管理及并發(fā)處理,同時(shí)還支持日志佛吓、監(jiān)控宵晚、任務(wù)重啟與跳過(guò)等特性垂攘,大大簡(jiǎn)化了批處理應(yīng)用開(kāi)發(fā),將開(kāi)發(fā)人員從復(fù)雜的任務(wù)配置管理過(guò)程中解放出來(lái)淤刃,使他們可以更多地去關(guān)注核心的業(yè)務(wù)處理過(guò)程晒他。
Spring Batch把批處理簡(jiǎn)化為Job和Job step兩部分,在Job step中逸贾,把數(shù)據(jù)處理分為讀數(shù)據(jù)(Reader)仪芒、處理數(shù)據(jù)(Processor)、寫數(shù)據(jù)(Writer)三個(gè)步驟耕陷,異常處理機(jī)制分為跳過(guò)掂名、重試、重啟三種哟沫,作業(yè)方式分為多線程饺蔑、并行、遠(yuǎn)程嗜诀、分區(qū)四種猾警。開(kāi)發(fā)者在開(kāi)發(fā)過(guò)程中,大部分工作是根據(jù)業(yè)務(wù)要求編寫Reader隆敢、Processor和Writer即可发皿,提高了批處理開(kāi)發(fā)的效率。同時(shí)Spring Batch本身也提供了很多默認(rèn)的Reader和Writer拂蝎,開(kāi)箱即用穴墅。
官網(wǎng)詳細(xì)介紹:https://spring.io/projects/spring-batch
<br/>
<br/>
### 架構(gòu)組件分類
<br/>
* Application(應(yīng)用層):包含開(kāi)發(fā)者應(yīng)用Spring-batch編寫的所有批處理作業(yè)和自定義代碼;
* Batch Core(核心層):包含加載和控制批處理作業(yè)所必需的核心類,它包含了Job,Step,JobLauncher的實(shí)現(xiàn);
* Infrastructure(基礎(chǔ)架構(gòu)層):基礎(chǔ)架構(gòu)層包含了Reader(ItemReader),Writer(ItemWriter),Services可以被應(yīng)用層和核心層使用;
<br/>
![file](https://graph.baidu.com/resource/222c98d8004610c7abdea01583336385.png)
<br/>
<br/>
### 優(yōu)勢(shì)
<br/>
* 豐富的開(kāi)箱即用組件 開(kāi)箱即用組件包括各種資源的讀温自、寫玄货。讀/寫:支持文本文件讀/寫、XML文件讀/寫悼泌、數(shù)據(jù)庫(kù)讀/寫松捉、JMS隊(duì)列讀/寫等。還提供作業(yè)倉(cāng)庫(kù)馆里,作業(yè)調(diào)度器等基礎(chǔ)設(shè)施隘世,大大簡(jiǎn)化開(kāi)發(fā)復(fù)雜度。
* 面向chunk處理 支持多次讀鸠踪、一次寫丙者、避免多次對(duì)資源的寫入,大幅提升批處理效率慢哈。
* 事務(wù)管理能力 默認(rèn)采用Spring提供的聲明式事務(wù)管理模型蔓钟,面向Chunk的操作支持事務(wù)管理,同時(shí)支持為每個(gè)tasklet操作設(shè)置細(xì)粒度的事務(wù)配置:隔離級(jí)別卵贱、傳播行為滥沫、超時(shí)設(shè)置等侣集。
* 元數(shù)據(jù)管理 自動(dòng)記錄Job和Step的執(zhí)行情況、包括成功兰绣、失敗世分、失敗的異常信息、執(zhí)行次數(shù)缀辩、重試次數(shù)臭埋、跳過(guò)次數(shù)、執(zhí)行時(shí)間等臀玄,方便后期的維護(hù)和查看瓢阴。
* 易監(jiān)控的批處理應(yīng)用 提供了靈活的監(jiān)控模式,包括直接查看數(shù)據(jù)庫(kù)健无、通過(guò)Spring Batch提供的API查看荣恐、JMX控制臺(tái)查看等。其中還說(shuō)到Spring Batch Admin累贤,不過(guò)這個(gè)項(xiàng)目已不維護(hù)叠穆,改為用Spring Cloud Data Flow了。
* 豐富的流程定義 支持順序任務(wù)臼膏、條件分支任務(wù)硼被、基于這兩種任務(wù)可以組織復(fù)雜的任務(wù)流程。
* 健壯的批處理應(yīng)用 支持作業(yè)的跳過(guò)渗磅、重試嚷硫、重啟能力、避免因錯(cuò)誤導(dǎo)致批處理作業(yè)的異常中斷夺溢。
* 易擴(kuò)展的批處理應(yīng)用 擴(kuò)展機(jī)制包括多線程執(zhí)行一個(gè)Step(Multithreaded step)论巍、多線程并行執(zhí)行多個(gè)Step(Parallelizing step)烛谊、遠(yuǎn)程執(zhí)行作業(yè)(Remote chunking)风响、分區(qū)執(zhí)行(partitioning step)蹭睡。
* 復(fù)用企業(yè)現(xiàn)有IT資產(chǎn) 提供多種Adapter能力里逆,使得企業(yè)現(xiàn)有的服務(wù)可以方便集成到批處理應(yīng)用中挑豌。避免重新開(kāi)發(fā)磕蛇、達(dá)到復(fù)用企業(yè)遺留的服務(wù)資產(chǎn)登疗。
<br/>
<br/>
### 使用場(chǎng)景
<br/>
* 定期提交批處理任務(wù)
* 并行批處理
* 企業(yè)消息驅(qū)動(dòng)處理
* 大規(guī)模并行批處理
* 失敗后手動(dòng)或定時(shí)重啟
* 按順序處理依賴的任務(wù)(可擴(kuò)展為工作流驅(qū)動(dòng)的批處理)
* 部分處理:跳過(guò)記錄(例如碾盐,回滾時(shí))
* 批處理事務(wù)
<br/>
<br/>
### 原則與建議
<br/>
當(dāng)我們構(gòu)建一個(gè)批處理的過(guò)程時(shí)备韧,必須注意以下原則:
1辞居、通常情況下焙矛,批處理的過(guò)程對(duì)系統(tǒng)和架構(gòu)的設(shè)計(jì)要夠要求比較高葫盼,因此盡可能的使用通用架構(gòu)來(lái)處理批量數(shù)據(jù)處理,降低問(wèn)題發(fā)生的可能性村斟。Spring Batch是一個(gè)是一個(gè)輕量級(jí)的框架贫导,適用于處理一些靈活并沒(méi)有到海量的數(shù)據(jù)抛猫。
2、批處理應(yīng)該盡可能的簡(jiǎn)單孩灯,盡量避免在單個(gè)批處理中去執(zhí)行過(guò)于復(fù)雜的任務(wù)闺金。我們可以將任務(wù)分成多個(gè)批處理或者多個(gè)步驟去實(shí)現(xiàn)。
3峰档、保證數(shù)據(jù)處理和物理數(shù)據(jù)緊密相連败匹。籠統(tǒng)的說(shuō)就是我們?cè)谔幚頂?shù)據(jù)的過(guò)程中有很多步驟讥巡,在某些步驟執(zhí)行完時(shí)應(yīng)該就寫入數(shù)據(jù)掀亩,而不是等所有都處理完。
4欢顷、盡可能減少系統(tǒng)資源的使用归榕、尤其是耗費(fèi)大量資源的IO以及跨服務(wù)器引用,盡量分配好數(shù)據(jù)處理的批次吱涉。
5刹泄、定期分析系統(tǒng)的IO使用情況、SQL語(yǔ)句的執(zhí)行情況等怎爵,盡可能的減少不必要的IO操作特石。優(yōu)化的原則有:
* 盡量在一次事物中對(duì)同一數(shù)據(jù)進(jìn)行讀取或?qū)懢彺妗?/p>
* 一次事物中,盡可能在開(kāi)始就讀取所有需要使用的數(shù)據(jù)鳖链。
* 優(yōu)化索引姆蘸,觀察SQL的執(zhí)行情況,盡量使用主鍵索引芙委,盡量避免全表掃描或過(guò)多的索引掃描逞敷。
* SQL中的where盡可能通過(guò)主鍵查詢。
6灌侣、不要在批處理中對(duì)相同的數(shù)據(jù)執(zhí)行2次相同的操作推捐。
7、對(duì)于批處理程序而言應(yīng)該在批處理啟動(dòng)之前就分配足夠的內(nèi)存侧啼,以免處理的過(guò)程中去重新申請(qǐng)新的內(nèi)存頁(yè)牛柒。
8、對(duì)數(shù)據(jù)的完整性應(yīng)該從最差的角度來(lái)考慮痊乾,每一步的處理都應(yīng)該建立完備的數(shù)據(jù)校驗(yàn)皮壁。
9、對(duì)于數(shù)據(jù)的總量我們應(yīng)該有一個(gè)和數(shù)據(jù)記錄在數(shù)據(jù)結(jié)構(gòu)的某個(gè)字段 上哪审。
10蛾魄、所有的批處理系統(tǒng)都需要進(jìn)行壓力測(cè)試。
11、如果整個(gè)批處理的過(guò)程是基于文件系統(tǒng)滴须,在處理的過(guò)程中請(qǐng)切記完成文件的備份以及文件內(nèi)容的校驗(yàn)缴川。
<br/><br/>
### 通用策略
<br/>
和軟件開(kāi)發(fā)的設(shè)計(jì)模式一樣,批處理也有各種各樣的現(xiàn)成模式可供參考描馅。當(dāng)一個(gè)開(kāi)發(fā)(設(shè)計(jì))人員開(kāi)始執(zhí)行批處理任務(wù)時(shí)把夸,應(yīng)該將業(yè)務(wù)邏輯拆分為一下的步驟或者板塊分批執(zhí)行:
* 數(shù)據(jù)轉(zhuǎn)換:某個(gè)(某些)批處理的外部數(shù)據(jù)可能來(lái)自不同的外部系統(tǒng)或者外部提供者,這些數(shù)據(jù)的結(jié)構(gòu)千差萬(wàn)別铭污。在統(tǒng)一進(jìn)行批量數(shù)據(jù)處理之前需要對(duì)這些數(shù)據(jù)進(jìn)行轉(zhuǎn)換恋日,合并為一個(gè)統(tǒng)一的結(jié)構(gòu)。因此在數(shù)據(jù)開(kāi)始真正的執(zhí)行業(yè)務(wù)處理之前嘹狞,先要使用其他的方法或者一些批處理任務(wù)將這些數(shù)據(jù)轉(zhuǎn)換為統(tǒng)一的格式岂膳。
* 數(shù)據(jù)校驗(yàn):批處理是對(duì)大量數(shù)據(jù)進(jìn)行處理,并且數(shù)據(jù)的來(lái)源千差萬(wàn)別磅网,所以批處理的輸入數(shù)據(jù)需要對(duì)數(shù)據(jù)的完整性性進(jìn)行校驗(yàn)(比如校驗(yàn)字段數(shù)據(jù)是否缺失)谈截。另外批處理輸出的數(shù)據(jù)也需要進(jìn)行合適的校驗(yàn)(例如處理了100條數(shù)據(jù),校驗(yàn)100條數(shù)據(jù)是否校驗(yàn)成功)
* 提取數(shù)據(jù):批處理的工作是逐條從數(shù)據(jù)庫(kù)或目標(biāo)文件讀取記錄(records),提取時(shí)可以通過(guò)一些規(guī)則從數(shù)據(jù)源中進(jìn)行數(shù)據(jù)篩選涧偷。
* 數(shù)據(jù)實(shí)時(shí)更新處理:根據(jù)業(yè)務(wù)要求簸喂,對(duì)實(shí)時(shí)數(shù)據(jù)進(jìn)行處理。某些時(shí)候一行數(shù)據(jù)記錄的處理需要綁定在一個(gè)事物之下燎潮。
* 輸出記錄到標(biāo)準(zhǔn)的文檔格式:數(shù)據(jù)處理完成之后需要根據(jù)格式寫入到對(duì)應(yīng)的外部數(shù)據(jù)系統(tǒng)中喻鳄。
以上五個(gè)步驟是一個(gè)標(biāo)準(zhǔn)的數(shù)據(jù)批處理過(guò)程,Spring batch框架為業(yè)務(wù)實(shí)現(xiàn)提供了以上幾個(gè)功能入口确封。
<br/><br/>
### 數(shù)據(jù)額外處理
<br/>
某些情況需要實(shí)現(xiàn)對(duì)數(shù)據(jù)進(jìn)行額外處理除呵,在進(jìn)入批處理之前通過(guò)其他方式將數(shù)據(jù)進(jìn)行處理。主要內(nèi)容有:
排序:由于批處理是以獨(dú)立的行數(shù)據(jù)(record)進(jìn)行處理的爪喘,在處理的時(shí)候并不知道記錄前后關(guān)系颜曾。因此如果需要對(duì)整體數(shù)據(jù)進(jìn)行排序,最好事先使用其他方式完成秉剑。
分割:數(shù)據(jù)拆分也建議使用獨(dú)立的任務(wù)來(lái)完成泛豪。理由類似排序,因?yàn)榕幚淼倪^(guò)程都是以行記錄為基本處理單位的秃症,無(wú)法再對(duì)分割之后的數(shù)據(jù)進(jìn)行擴(kuò)展處理候址。
合并:理由如上。
<br/>
<br/>
# Spring Batch核心概念
<br/>
![file](https://graph.baidu.com/resource/222b3d02c776c03ca914f01583201124.png)
<br/>
Spring Batch在基礎(chǔ)架構(gòu)層种柑,把任務(wù)抽象為Job和Step,一個(gè)Job由多個(gè)Step來(lái)完成匹耕,step就是每個(gè)job要執(zhí)行的單個(gè)步驟聚请。
1、Job:是一個(gè)接口,接口中定義了一個(gè)作業(yè)是怎么樣執(zhí)行的
2驶赏、JobInstance:是job的一次執(zhí)行炸卑,一個(gè)JobInstance可重復(fù)執(zhí)行,如果上一次執(zhí)行失敗下次執(zhí)行的時(shí)候還會(huì)重新執(zhí)行上次失敗的job煤傍,每一次執(zhí)行就是一個(gè)JobExceution
3盖文、JobParameters:作為參數(shù)可以用來(lái)啟動(dòng)Job,并且可以用來(lái)標(biāo)識(shí)不同的Job,運(yùn)行時(shí)提供給JobInstance,jonExceution根據(jù)狀態(tài)和參數(shù)決定下一次是否繼續(xù)執(zhí)行
4蚯姆、JobExceution:每一次嘗試執(zhí)行一個(gè)Job的時(shí)候五续,我們就可以將其稱為一個(gè)JobExceution,這個(gè)執(zhí)行的結(jié)果可以為成功龄恋,也可以為失敗疙驾,例如一個(gè)JobInstance執(zhí)行失敗了,下一次執(zhí)行他傳入的參數(shù)是上次執(zhí)行的時(shí)間郭毕,他將會(huì)繼續(xù)執(zhí)行它碎,這樣始終執(zhí)行的是一個(gè)JobInstance,而產(chǎn)生了兩個(gè)JobExceution
5显押、Step:主要分為兩塊
(1)Tasklet:是一個(gè)任務(wù)單元扳肛,它是屬于可以重復(fù)利用的東西。接口其中包含了一個(gè)唯一的方法execute();
(2)Chunk-based:chunk就是數(shù)據(jù)塊乘碑,你需要定義多大的數(shù)據(jù)量是一個(gè)chunk敞峭。Chunk里面就是不斷循環(huán)的一個(gè)流程,讀數(shù)據(jù)蝉仇,處理數(shù)據(jù)旋讹,然后寫數(shù)據(jù)。Spring Batch會(huì)不斷的循環(huán)這個(gè)流程轿衔,直到批處理數(shù)據(jù)完成沉迹。
* ·itemReader:數(shù)據(jù)輸入input:對(duì)于一個(gè)Step而言,每次讀取一個(gè)條目害驹;
* ·itemProcessor:數(shù)據(jù)處理processing
* ·ItemWriter:數(shù)據(jù)輸出output:對(duì)于一個(gè)Step而言鞭呕,每次根據(jù)設(shè)定輸出批量一個(gè)條目;
6宛官、StepExecution:一個(gè)Step的每一次嘗試執(zhí)行葫松,都會(huì)創(chuàng)建一個(gè)StepExection,在一個(gè)Step實(shí)際開(kāi)始執(zhí)行的時(shí)候創(chuàng)建
7、ExecutionContext:執(zhí)行上下文底洗,代表的是一個(gè)key-value鍵值對(duì)的集合腋么,可以被Spring框架進(jìn)行在持久化管理,能夠是開(kāi)發(fā)人員存儲(chǔ)持久化狀態(tài)亥揖,每一個(gè)JobExecution以及每一個(gè)StepExecution的執(zhí)行都會(huì)對(duì)應(yīng)一個(gè)執(zhí)行上下文(ExecutionContext);對(duì)于StepExecution在每一次提交點(diǎn)時(shí)就會(huì)保存一下執(zhí)行上下文珊擂,而對(duì)于Job是在每一個(gè)StepExecution執(zhí)行之間進(jìn)行保存圣勒,例如,我們從Step1換到Step2是就會(huì)保存;
8摧扇、JobLauncher:接口圣贸,用于啟動(dòng)和加載Job,根據(jù)傳入的參數(shù)進(jìn)行啟動(dòng),返回Job一次執(zhí)行的情況
9扛稽、JobRepository:Job及Job的運(yùn)行結(jié)果和狀態(tài)吁峻、Step的運(yùn)行結(jié)果和狀態(tài),都會(huì)保存在JobRepository中在张。
<br/>
![file](https://graph.baidu.com/resource/22292f890320f14813a4f01583205437.png)
<br/>
<br/>
概念說(shuō)明可見(jiàn)下表:
|領(lǐng)域?qū)ο髚描述|
|:--:|:--|
|JobRepository|作業(yè)倉(cāng)庫(kù)用含,保存Job、Step執(zhí)行過(guò)程中的狀態(tài)及結(jié)果|
|JobLauncher|作業(yè)執(zhí)行器瞧掺,是執(zhí)行Job的入口|
|Job|一個(gè)批處理任務(wù)耕餐,由一個(gè)或多個(gè)Step組成|
|Step|一個(gè)任務(wù)的具體的執(zhí)行邏輯單位|
|Item|一條數(shù)據(jù)記錄|
|ItemReader|從數(shù)據(jù)源讀數(shù)據(jù)|
|ItemProcessor|對(duì)數(shù)據(jù)進(jìn)行處理,如數(shù)據(jù)清洗辟狈、轉(zhuǎn)換肠缔、過(guò)濾、校驗(yàn)等|
|ItemWriter|寫入數(shù)據(jù)到指定目標(biāo)|
|Chunk|給定數(shù)量的Item集合哼转,如讀取到chunk數(shù)量后明未,才進(jìn)行寫操作|
|Tasklet|Step中具體執(zhí)行邏輯,可重復(fù)執(zhí)行|
<br/>
<br/>
## Spring Batch數(shù)據(jù)表
![file](https://graph.baidu.com/resource/222af401c706e235dced301583205536.png)
batch_job_instance:這張表能看到每次運(yùn)行的job名字壹蔓。
![file](https://graph.baidu.com/resource/222ff1a85e8bb9bdd400501583205573.png)
batch_job_execution:這張表能看到每次運(yùn)行job的開(kāi)始時(shí)間趟妥,結(jié)束時(shí)間,狀態(tài)佣蓉,以及失敗后的錯(cuò)誤消息是什么披摄。
![file](https://graph.baidu.com/resource/2223585fcfbc04644597301583205581.png)
batch_step_execution:這張表你能看到更多關(guān)于step的詳細(xì)信息。比如step的開(kāi)始時(shí)間勇凭,結(jié)束時(shí)間疚膊,提交次數(shù),讀寫次數(shù)虾标,狀態(tài)寓盗,以及失敗后的錯(cuò)誤信息等。
圖片描述
<br/>
<br/>
## Job
<br/>
簡(jiǎn)單的說(shuō)Job是封裝一個(gè)批處理過(guò)程的實(shí)體璧函,與其他的Spring項(xiàng)目類似傀蚌,Job可以通過(guò)XML或Java類配置,稱為“Job Configuration”蘸吓。如下圖Job是單個(gè)批處理的最頂層善炫。
<br/>
為了便于理解,可以簡(jiǎn)單的將Job理解為是每一步(Step)實(shí)例的容器美澳。他結(jié)合了多個(gè)Step销部,為它們提供統(tǒng)一的服務(wù)同時(shí)也為Step提供個(gè)性化的服務(wù)摸航,比如步驟重啟制跟。通常情況下Job的配置包含以下內(nèi)容:
* Job的名稱
* 定義和排序Step執(zhí)行實(shí)例舅桩。
* 標(biāo)記每個(gè)Step是否可以重啟。
<br/>
Spring Batch為Job接口提供了默認(rèn)的實(shí)現(xiàn)——SimpleJob雨膨,其中實(shí)現(xiàn)了一些標(biāo)準(zhǔn)的批處理方法擂涛。下面的代碼展示了如可注入一個(gè)Job。
```
@Bean
public Job testJob() {
? ? return this.jobBuilderFactory.get("testJob") //get中命名了Job的名稱
? ? ? ? ? ? ? ? ? ? .start(stepOne())?
? ? ? ? ? ? ? ? ? ? .next(stepTwo())
? ? ? ? ? ? ? ? ? ? .next(stepThree())
? ? ? ? ? ? ? ? ? ? .end()
? ? ? ? ? ? ? ? ? ? .build();
}
```
<br/>
### JobInstance
<br/>
JobInstance是指批處理作業(yè)運(yùn)行的實(shí)例聊记。
例如一個(gè)批處理必須在每天執(zhí)行一次撒妈,系統(tǒng)在2019年5月1日?qǐng)?zhí)行了一次我們稱之為2019-05-01的實(shí)例,類似的還會(huì)有2019-05-02排监、2019-05-03實(shí)例狰右。
通常情況下,一個(gè)JobInstance對(duì)應(yīng)一個(gè)JobParameters舆床,對(duì)應(yīng)多個(gè)JobExecution棋蚌。(JobParameters、JobExecution見(jiàn)后文)挨队。同一個(gè)JobInstance具有相同的上下文(ExecutionContext內(nèi)容見(jiàn)后文)谷暮。
<br/>
### JobParameters
<br/>
前面討論了JobInstance與Job的區(qū)別,但是具體的區(qū)別內(nèi)容都是通過(guò)JobParameters體現(xiàn)的盛垦。一個(gè)JobParameters對(duì)象中包含了一系列Job運(yùn)行相關(guān)的參數(shù)湿弦,這些參數(shù)可以用于參考或者用于實(shí)際的業(yè)務(wù)使用。對(duì)應(yīng)的關(guān)系如下圖:
![file](https://graph.baidu.com/resource/2224a04a7563694a8af6301583202663.png)
<br/>
當(dāng)我們執(zhí)行2個(gè)不同的JobInstance時(shí)JobParameters中的屬性都會(huì)有差異腾夯〖瞻#可以簡(jiǎn)單的認(rèn)為一個(gè)JobInstance的標(biāo)識(shí)就是Job+JobParameters。
<br/>
### JobExecution
<br/>
JobExecution可以理解為單次運(yùn)行Job的容器蝶俱。一次JobInstance執(zhí)行的結(jié)果可能是成功班利、也可能是失敗。但是對(duì)于Spring Batch框架而言跷乐,只有返回運(yùn)行成功才會(huì)視為完成一次批處理肥败。
例如2019-05-01執(zhí)行了一次JobInstance,但是執(zhí)行的過(guò)程失敗愕提,因此第二次還會(huì)有一個(gè)“相同的”的JobInstance被執(zhí)行馒稍。
Job用于定義批處理如何執(zhí)行,JobInstance純粹的就是一個(gè)處理對(duì)象浅侨,把所有的運(yùn)行內(nèi)容和信息組織在一起纽谒,主要是為了當(dāng)面臨問(wèn)題時(shí)定義正確的重啟參數(shù)。而JobExecution是運(yùn)行時(shí)的“容器”如输,記錄動(dòng)態(tài)運(yùn)行時(shí)的各種屬性和上線文鼓黔。
<br/>
他包括的信息有:
|屬性| 說(shuō)明|
|:--:|:--:|
|status| 狀態(tài)類名為BatchStatus央勒,它指示了執(zhí)行的狀態(tài)。在執(zhí)行的過(guò)程中狀態(tài)為BatchStatus#STARTED澳化,失敶薏健:BatchStatus#FAILED,完成:BatchStatus#COMPLETED|
|startTime| java.util.Date對(duì)象缎谷,標(biāo)記批處理任務(wù)啟動(dòng)的系統(tǒng)時(shí)間井濒,批處理任務(wù)未啟動(dòng)數(shù)據(jù)為空|
|endTime| java.util.Date對(duì)象,結(jié)束時(shí)間無(wú)論是否成功都包含該數(shù)據(jù)列林,如未處理完為空|
|exitStatus| ExitStatus類瑞你,記錄運(yùn)行結(jié)果。|
|createTime| java.util.Date,JobExecution的創(chuàng)建時(shí)間希痴,某些使用execution已經(jīng)創(chuàng)建但是并未開(kāi)始運(yùn)行者甲。|
|lastUpdate| java.util.Date,最后一次更新時(shí)間|
|executionContext| 批處理任務(wù)執(zhí)行的所有用戶數(shù)據(jù)|
|failureExceptions| 記錄在執(zhí)行Job時(shí)的異常砌创,對(duì)于排查問(wèn)題非常有用|
<br/>
以上這些內(nèi)容Spring Batch都會(huì)通過(guò)JobRepository進(jìn)行持久化(這些信息官方文成稱之為MetaData)虏缸,因此在對(duì)應(yīng)的數(shù)據(jù)源中可以看到下列信息:
BATCH_JOB_INSTANCE:
|JOB_INST_ID| JOB_NAME|
|:--:|:--:|
|1| EndOfDayJob|
<br/>
BATCH_JOB_EXECUTION_PARAMS:
|JOB_EXECUTION_ID| TYPE_CD| KEY_NAME| DATE_VAL| IDENTIFYING|
|:--:|:--:|
|1| DATE| schedule.Date| 2019-01-01| TRUE|
<br/>
BATCH_JOB_EXECUTION:
|JOB_EXEC_ID| JOB_INST_ID| START_TIME| END_TIME| STATUS|
|:--:|:--:|
|1| 1| 2019-01-01 21:00| 2017-01-01 21:30| FAILED|
<br/>
當(dāng)某個(gè)Job批處理任務(wù)失敗之后會(huì)在對(duì)應(yīng)的數(shù)據(jù)庫(kù)表中路對(duì)應(yīng)的狀態(tài)。假設(shè)1月1號(hào)執(zhí)行的任務(wù)失敗纺铭,技術(shù)團(tuán)隊(duì)花費(fèi)了大量的時(shí)間解決這個(gè)問(wèn)題到了第二天才繼續(xù)執(zhí)行這個(gè)任務(wù)寇钉。
BATCH_JOB_INSTANCE:
|JOB_INST_ID| JOB_NAME|
|:--:|:--:|
|1| EndOfDayJob|
|2| EndOfDayJob|
<br/>
BATCH_JOB_EXECUTION_PARAMS:
|JOB_EXECUTION_ID| TYPE_CD| KEY_NAME| DATE_VAL| IDENTIFYING|
|:--:|:--:|
|1| DATE| schedule.Date| 2019-01-01| TRUE|
|2| DATE| schedule.Date| 2019-01-01| TRUE|
|3| DATE| schedule.Date| 2019-01-02| TRUE|
<br/>
BATCH_JOB_EXECUTION:
|JOB_EXEC_ID| JOB_INST_ID| START_TIME| END_TIME| STATUS|
|:--:|:--:|
|1| 1| 2019-01-01 21:00| 2017-01-01 21:30| FAILED|
|2| 1| 2019-01-02 21:00| 2017-01-02 21:30| COMPLETED|
|3| 2| 2019-01-02 21:31| 2017-01-02 22:29| COMPLETED|
<br/>
從數(shù)據(jù)上看好似JobInstance是一個(gè)接一個(gè)順序執(zhí)行的,但是對(duì)于Spring Batch并沒(méi)有進(jìn)行任何控制舶赔。不同的JobInstance很有可能是同時(shí)在運(yùn)行(相同的JobInstance同時(shí)運(yùn)行會(huì)拋出JobExecutionAlreadyRunningException異常)扫倡。
<br/>
<br/>
## Step
<br/>
Step是批處理重復(fù)運(yùn)行的最小單元,它按照順序定義了一次執(zhí)行的必要過(guò)程竟纳。
因此每個(gè)Job可以視作由一個(gè)或多個(gè)多個(gè)Step組成撵溃。一個(gè)Step包含了所有所有進(jìn)行批處理的必要信息,這些信息的內(nèi)容是由開(kāi)發(fā)人員決定的并沒(méi)有統(tǒng)一的標(biāo)準(zhǔn)锥累。一個(gè)Step可以很簡(jiǎn)單缘挑,也可以很復(fù)雜。他可以是復(fù)雜業(yè)務(wù)的組合桶略,也有可能僅僅用于遷移數(shù)據(jù)语淘。
與JobExecution的概念類似,Step也有特定的StepExecution际歼,關(guān)系結(jié)構(gòu)如下:
![file](https://graph.baidu.com/resource/222013099b468a0c1cbef01583203250.png)
<br/><br/>
### StepExecution
<br/>
StepExecution表示單次執(zhí)行Step的容器惶翻,每次Step執(zhí)行時(shí)都會(huì)有一個(gè)新的StepExecution被創(chuàng)建。與JobExecution不同的是鹅心,當(dāng)某個(gè)Step執(zhí)行失敗后默認(rèn)并不會(huì)重新執(zhí)行吕粗。StepExecution包含以下屬性:
|屬性| 說(shuō)明|
|:--:|:--:|
|status| 狀態(tài)類名為BatchStatus,它指示了執(zhí)行的狀態(tài)旭愧。在執(zhí)行的過(guò)程中狀態(tài)為BatchStatus#STARTED颅筋,失斨嫦尽:BatchStatus#FAILED,完成:BatchStatus#COMPLETED|
|startTime| java.util.Date對(duì)象议泵,標(biāo)記StepExecution啟動(dòng)的系統(tǒng)時(shí)間占贫,未啟動(dòng)數(shù)據(jù)為空|
|endTime| java.util.Date對(duì)象,結(jié)束時(shí)間肢簿,無(wú)論是否成功都包含該數(shù)據(jù)靶剑,如未處理完為空|
|exitStatus| ExitStatus類蜻拨,記錄運(yùn)行結(jié)果池充。|
|createTime| java.util.Date,JobExecution的創(chuàng)建時(shí)間,某些使用execution已經(jīng)創(chuàng)建但是并未開(kāi)始運(yùn)行缎讼。|
|lastUpdate| java.util.Date收夸,最后一次更新時(shí)間|
|executionContext| 批處理任務(wù)執(zhí)行的所有用戶數(shù)據(jù)|
|readCount| 成功讀取數(shù)據(jù)的次數(shù)|
|wirteCount| 成功寫入數(shù)據(jù)的次數(shù)|
|commitCount| 成功提交數(shù)據(jù)的次數(shù)|
|rollbackCount| 回歸數(shù)據(jù)的次數(shù),有業(yè)務(wù)代碼觸發(fā)|
|readSkipCount| 當(dāng)讀數(shù)據(jù)發(fā)生錯(cuò)誤時(shí)跳過(guò)處理的次數(shù)|
|processSkipCount| 當(dāng)處理過(guò)程發(fā)生錯(cuò)誤血崭,跳過(guò)處理的次數(shù)|
|filterCount| 被過(guò)濾規(guī)則攔截未處理的次數(shù)|
|writeSkipCount| 寫數(shù)據(jù)失敗卧惜,跳過(guò)處理的次數(shù)|
<br/><br/>
## ExecutionContext
<br/>
前文已經(jīng)多次提到ExecutionContext〖腥遥可以簡(jiǎn)單的認(rèn)為ExecutionContext提供了一個(gè)Key/Value機(jī)制咽瓷,在StepExecution和JobExecution對(duì)象的任何位置都可以獲取到ExecutionContext中的任何數(shù)據(jù)。最有價(jià)值的作用是記錄數(shù)據(jù)的執(zhí)行位置舰讹,以便發(fā)生重啟時(shí)候從對(duì)應(yīng)的位置繼續(xù)執(zhí)行:
```
executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition())
```
<br/>
比如在任務(wù)中有一個(gè)名為“l(fā)oadData”的Step茅姜,他的作用是從文件中讀取數(shù)據(jù)寫入到數(shù)據(jù)庫(kù),當(dāng)?shù)谝淮螆?zhí)行失敗后月匣,數(shù)據(jù)庫(kù)中有如下數(shù)據(jù):
BATCH_JOB_INSTANCE:
| JOB_INST_ID| JOB_NAME|
|:--:|:--:|
| 1| EndOfDayJob|
<br/>
BATCH_JOB_EXECUTION_PARAMS:
| JOB_INST_ID| TYPE_CD| KEY_NAME| DATE_VAL|
|:--:|:--:|
| 1| DATE| schedule.Date| 2019-01-01|
<br/>
BATCH_JOB_EXECUTION:
| JOB_EXEC_ID| JOB_INST_ID| START_TIME| END_TIME| STATUS|
|:--:|:--:|
| 1|? 1 | 2017-01-01 21:00| 2017-01-01 21:30| FAILED|
<br/>
BATCH_STEP_EXECUTION:
|STEP_EXEC_ID| JOB_EXEC_ID| STEP_NAME| START_TIME| END_TIME| STATUS|
|:--:|:--:|
|1| 1| loadData| 2017-01-01 21:00| 2017-01-01 21:30| FAILED|
<br/>
BATCH_STEP_EXECUTION_CONTEXT:
|STEP_EXEC_ID|SHORT_CONTEXT|
|:---:|:---:|
|1|{piece.count=40321}|
<br/>
在上面的例子中钻洒,Step運(yùn)行30分鐘處理了40321個(gè)“pieces”,我們姑且認(rèn)為“pieces”表示行間的行數(shù)(實(shí)際就是每個(gè)Step完成循環(huán)處理的個(gè)數(shù))锄开。
這個(gè)值會(huì)在每個(gè)commit之前被更新記錄在ExecutionContext中(更新需要用到StepListener后文會(huì)詳細(xì)說(shuō)明)素标。
當(dāng)我們?cè)俅沃貑⑦@個(gè)Job時(shí)并記錄在BATCH_STEP_EXECUTION_CONTEXT中的數(shù)據(jù)會(huì)加載到ExecutionContext中,這樣當(dāng)我們繼續(xù)執(zhí)行批處理任務(wù)時(shí)可以從上一次中斷的位置繼續(xù)處理。
例如下面的代碼在ItemReader中檢查上次執(zhí)行的結(jié)果萍悴,并從中斷的位置繼續(xù)執(zhí)行:
```
if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
? ? log.debug("Initializing for restart. Restart data is: " + executionContext);
? ? long lineCount = executionContext.getLong(getKey(LINES_READ_COUNT));
? ? LineReader reader = getReader();
? ? Object record = "";
? ? while (reader.getPosition() < lineCount && record != null) {
? ? ? ? record = readLine();
? ? }
}
```
<br/>
<br/>
ExecutionContext是根據(jù)JobInstance進(jìn)行管理的头遭,因此只要是相同的實(shí)例都會(huì)具備相同的ExecutionContext(無(wú)論是否停止)。此外通過(guò)以下方法都可以獲得一個(gè)ExecutionContext:
```
ExecutionContext ecStep = stepExecution.getExecutionContext();
ExecutionContext ecJob = jobExecution.getExecutionContext();
```
<br/>
但是這2個(gè)ExecutionContext并不相同癣诱,前者是在一個(gè)Step中每次Commit數(shù)據(jù)之間共享计维,后者是在Step與Step之間共享。
<br/><br/>
## JobRepository
<br/>
JobRepository是所有前面介紹的對(duì)象實(shí)例的持久化機(jī)制狡刘。他為JobLauncher享潜、Job、Step的實(shí)現(xiàn)提供了CRUD操作嗅蔬。當(dāng)一個(gè)Job第一次被啟動(dòng)時(shí)剑按,一個(gè)JobExecution會(huì)從數(shù)據(jù)源中獲取到疾就,同時(shí)在執(zhí)行的過(guò)程中StepExecution、JobExecution的實(shí)現(xiàn)都會(huì)記錄到數(shù)據(jù)源中艺蝴。使用@EnableBatchProcessing注解后JobRepository會(huì)進(jìn)行自動(dòng)化配置猬腰。
<br/>
## JobLauncher
<br/>
JobLauncher為Job的啟動(dòng)運(yùn)行提供了一個(gè)邊界的入口,在啟動(dòng)Job的同時(shí)還可以定制JobParameters:
```
public interface JobLauncher {
public JobExecution run(Job job, JobParameters jobParameters)
throws JobExecutionAlreadyRunningException, JobRestartException,
? JobInstanceAlreadyCompleteException, JobParametersInvalidException;
}
```
參考:
https://my.oschina.net/mianshenglee/blog/3058569