Перейти к основному содержимому

Настройка логики пропуска в Spring Batch

· 4 мин. чтения

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. Настройка лимита пропуска и исключений с возможностью пропуска

3.1. Использование пропусков и пропусков

Давайте теперь обсудим первый из двух способов настройки нашего задания для пропуска элементов в случае сбоя — методы skip и skipLimit :

@Bean
public Step skippingStep(
ItemProcessor<Transaction, Transaction> processor,
ItemWriter<Transaction> writer) throws ParseException {
return stepBuilderFactory
.get("skippingStep")
.<Transaction, Transaction>chunk(10)
.reader(itemReader(invalidInputCsv))
.processor(processor)
.writer(writer)
.faultTolerant()
.skipLimit(2)
.skip(MissingUsernameException.class)
.skip(NegativeAmountException.class)
.build();
}

Прежде всего, чтобы включить функцию пропуска, нам нужно включить вызов faultTolerant() во время процесса построения шага.

В функциях skip() и skipLimit() мы определяем исключения, которые хотим пропустить, и максимальное количество пропущенных элементов.

В приведенном выше примере, если на этапе чтения, обработки или записи создается исключение MissingUsernameException или NegativeAmountException , то текущий обрабатываемый элемент будет пропущен и будет учитываться общее ограничение, равное двум.

Следовательно, если какое-либо исключение будет вызвано в третий раз, то весь шаг завершится ошибкой .

3.1. Использование непропуска

В предыдущем примере любое другое исключение, кроме MissingUsernameException и NegativeAmountException , приводит к сбою нашего шага.

В некоторых ситуациях, однако, может быть более уместным определить исключения, которые должны привести к сбою нашего шага и пропуску любого другого.

Давайте посмотрим, как мы можем настроить это, используя skip , skipLimit и noSkip :

@Bean
public Step skippingStep(
ItemProcessor<Transaction, Transaction> processor,
ItemWriter<Transaction> writer) throws ParseException {
return stepBuilderFactory
.get("skippingStep")
.<Transaction, Transaction>chunk(10)
.reader(itemReader(invalidInputCsv))
.processor(processor)
.writer(writer)
.faultTolerant()
.skipLimit(2)
.skip(Exception.class)
.noSkip(SAXException.class)
.build();
}

В приведенной выше конфигурации мы указываем Spring Bat framework пропускать любое исключение (в пределах настроенного ограничения), кроме SAXException . Это означает , что SAXException всегда вызывает сбой шага.

Порядок вызовов skip() и noSkip() не имеет значения.

4. Использование пользовательской политики пропуска

Иногда нам может понадобиться более сложный механизм проверки пропусков. Для этой цели среда 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(
ItemProcessor<Transaction, Transaction> processor,
ItemWriter<Transaction> writer) throws ParseException {
return stepBuilderFactory
.get("skippingStep")
.<Transaction, Transaction>chunk(10)
.reader(itemReader(invalidInputCsv))
.processor(processor)
.writer(writer)
.faultTolerant()
.skipPolicy(new CustomSkipPolicy())
.build();
}

И, как и в нашем предыдущем примере, нам все еще нужно использовать faultTolerant() , чтобы включить функцию пропуска.

Однако на этот раз мы не вызываем skip() или noSkip() . Вместо этого мы используем метод skipPolicy() , чтобы предоставить собственную реализацию интерфейса SkipPolicy .

Как мы видим, такой подход дает нам больше гибкости, поэтому может быть хорошим выбором в определенных случаях использования .

5. Вывод

В этом руководстве мы представили два способа сделать задание Spring Batch отказоустойчивым.

Несмотря на то, что использование skipLimit() вместе с методами skip() и noSkip() кажется более популярным, в некоторых ситуациях реализация пользовательской SkipPolicy может оказаться более удобной.

Как обычно, все примеры кода доступны на GitHub .