1. 引言
默認情況下,在 Spring Batch 任務處理過程中遇到的任何錯誤都會導致相應的步驟失敗。然而,在許多情況下,我們更希望跳過當前處理的項目,以應對某些異常情況。
在本教程中,我們將探索兩種配置跳過邏輯的方法,這些方法適用於 Spring Batch 框架。
2. 我們的用例
為了便於示例説明,我們將重用在我們的 Spring Batch 介紹文章中已經展示的,一個簡單、基於塊的作業。
該作業將從 CSV 格式轉換一些財務數據為 XML 格式。
2.1. 輸入數據
首先,讓我們向原始 CSV 文件添加幾行數據:
username, user_id, transaction_date, transaction_amount
devendra, 1234, 31/10/2015, 10000
john, 2134, 3/12/2015, 12321
robin, 2134, 2/02/2015, 23411
, 2536, 3/10/2019, 100
mike, 9876, 5/11/2018, -500
, 3425, 10/10/2017, 9999如我們所見,最後三行包含一些無效數據——第5行和第7行缺少用户名字段,第6行的交易金額為負數。
在後續章節中,我們將配置批量作業以跳過這些損壞的記錄。
3. 配置跳過限制和可跳過異常
This section describes how to configure the skip limit and the list of skippable exceptions.
The skip limit defines the maximum number of records that can be skipped during a data load. This is useful when you need to skip certain records that are invalid or contain errors.
Skippable Exceptions:
The skippable exceptions list allows you to specify a list of records that should always be skipped, regardless of the skip limit. This is useful when you need to skip records that are consistently invalid or contain errors.
To configure the skip limit and the skippable exceptions, follow these steps:
- Navigate to the Configuration page.
- Select the Data Load configuration.
- Locate the Skip Limit and Skippable Exceptions settings.
- Enter the desired skip limit value.
- Add the records to the Skippable Exceptions list.
3.1. 使用 skip 和 skipLimit 方法
現在,我們來討論兩種配置工作項以在發生失敗時跳過項目的方案之一——skip 和 skipLimit 方法:
@Bean
public Step skippingStep(JobRepository jobRepository, PlatformTransactionManager transactionManager,
ItemProcessor<Transaction, Transaction> processor,
ItemWriter<Transaction> writer) throws ParseException {
return new StepBuilder("skippingStep", jobRepository)
.<Transaction, Transaction> chunk(10, transactionManager)
.reader(itemReader(invalidInputCsv))
.processor(processor)
.writer(writer)
.faultTolerant()
.skipLimit(2)
.skip(MissingUsernameException.class)
.skip(NegativeAmountException.class)
.build();
}首先,為了啓用跳過功能,需要在步驟構建過程中調用 faultTolerant()。
在 skip() 和 skipLimit() 中,我們定義了要跳過的異常以及跳過的最大數量。
在上面的示例中,如果在讀取、處理或寫入階段拋出 MissingUsernameException 或 NegativeAmountException 異常,則當前處理的項將被省略,並計入總限制的兩個項。
因此,如果任何異常被拋出第三次,則整個步驟將會失敗。
3.1. 使用 noSkip
在之前的示例中,除了 MissingUsernameException 和 NegativeAmountException 之外的任何其他異常都會導致我們的步驟失敗。
在某些情況下,更合適的是 識別出應該導致步驟失敗並跳過任何其他異常的異常。
讓我們看看如何使用 skip、skipLimit 和 noSkip 來配置它:
@Bean
public Step skippingStep(JobRepository jobRepository, PlatformTransactionManager transactionManager,
ItemProcessor<Transaction, Transaction> processor,
ItemWriter<Transaction> writer) throws ParseException {
return new StepBuilder("skippingStep", jobRepository)
.<Transaction, Transaction> chunk(10, transactionManager)
.reader(itemReader(invalidInputCsv))
.processor(processor)
.writer(writer)
.faultTolerant()
.skipLimit(2)
.skip(Exception.class)
.noSkip(SAXException.class)
.build();
}通過上述配置,我們指示 Spring Batch 框架在配置的限制內跳過任何 Exception(包括但不限於異常),但會處理 SAXException。 這意味着 SAXException 始終會導致步驟失敗。
skip() 和 noSkip() 調用之間的順序不重要。
4. 使用自定義 SkipPolicy
有時我們需要更復雜的跳過檢查機制。為此,Spring Batch 框架提供了 SkipPolicy 接口。
我們可以提供自己的跳過邏輯並將其插入到步驟定義中。
考慮到前面的示例,假設我們仍然希望定義跳過限制為兩個項目,並且僅使 MissingUsernameException 和 NegativeAmountException 可跳過。
但是,一個額外的約束是,我們可以跳過 NegativeAmountException,但前提是金額不超過定義的限制。
讓我們實現自定義的 SkipPolicy:
public class CustomSkipPolicy implements SkipPolicy {
private static final int MAX_SKIP_COUNT = 2;
private static final int INVALID_TX_AMOUNT_LIMIT = -1000;
@Override
public boolean shouldSkip(Throwable throwable, int skipCount)
throws SkipLimitExceededException {
if (throwable instanceof MissingUsernameException && skipCount < MAX_SKIP_COUNT) {
return true;
}
if (throwable instanceof NegativeAmountException && skipCount < MAX_SKIP_COUNT ) {
NegativeAmountException ex = (NegativeAmountException) throwable;
if(ex.getAmount() < INVALID_TX_AMOUNT_LIMIT) {
return false;
} else {
return true;
}
}
return false;
}
}現在,我們可以使用自定義策略在步驟定義中使用它:
@Bean
public Step skippingStep(JobRepository jobRepository, PlatformTransactionManager transactionManager,
ItemProcessor<Transaction, Transaction> processor,
ItemWriter<Transaction> writer) throws ParseException {
return new StepBuilder("skippingStep", jobRepository)
.<Transaction, Transaction> chunk(10, transactionManager)
.reader(itemReader(invalidInputCsv))
.processor(processor)
.writer(writer)
.faultTolerant()
.skipPolicy(new CustomSkipPolicy())
.build();
}與我們之前的示例類似,我們仍然需要使用 faultTolerant() 啓用跳過功能。
但這次,我們不再調用 skip() 或 noSkip()。相反,我們使用 skipPolicy() 方法來提供我們自己的 SkipPolicy 接口實現。
正如我們所見,這種 方法 給予我們更大的靈活性,因此在某些用例中可能是一個不錯的選擇。
5. 結論
在本教程中,我們介紹了兩種使 Spring Batch 作業具有容錯性的方法。
儘管使用 skipLimit() 方法與 skip() 和 noSkip() 方法組合似乎在某些情況下更受歡迎,但我們可能會發現實現自定義的 <em>SkipPolicy</em> 更方便。