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 .