Spring Batch 批處理(1) - 簡(jiǎn)介及使用場(chǎng)景

# 什么是 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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末猜敢,一起剝皮案震驚了整個(gè)濱河市姑荷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缩擂,老刑警劉巖鼠冕,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異胯盯,居然都是意外死亡懈费,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門博脑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)憎乙,“玉大人,你說(shuō)我怎么就攤上這事叉趣∨⒈撸” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵疗杉,是天一觀的道長(zhǎng)阵谚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乡数,這世上最難降的妖魔是什么椭蹄? 我笑而不...
    開(kāi)封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮净赴,結(jié)果婚禮上绳矩,老公的妹妹穿的比我還像新娘。我一直安慰自己玖翅,他們只是感情好翼馆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著金度,像睡著了一般应媚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猜极,一...
    開(kāi)封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天中姜,我揣著相機(jī)與錄音,去河邊找鬼。 笑死丢胚,一個(gè)胖子當(dāng)著我的面吹牛翩瓜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播携龟,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兔跌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了峡蟋?” 一聲冷哼從身側(cè)響起坟桅,我...
    開(kāi)封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蕊蝗,沒(méi)想到半個(gè)月后仅乓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匿又,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年方灾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碌更。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖洞慎,靈堂內(nèi)的尸體忽然破棺而出痛单,到底是詐尸還是另有隱情,我是刑警寧澤劲腿,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布旭绒,位于F島的核電站,受9級(jí)特大地震影響焦人,放射性物質(zhì)發(fā)生泄漏挥吵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一花椭、第九天 我趴在偏房一處隱蔽的房頂上張望忽匈。 院中可真熱鬧,春花似錦矿辽、人聲如沸丹允。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雕蔽。三九已至,卻和暖如春宾娜,著一層夾襖步出監(jiān)牢的瞬間批狐,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工前塔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嚣艇,地道東北人缘眶。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像髓废,于是被迫代替她去往敵國(guó)和親巷懈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容