Как осуществляется пропуск в Spring Batch?
Мне было интересно, как я мог определить в моем ItemWriter
независимо от того, находился ли Spring Batch в режиме обработки фрагментов или в резервном режиме обработки отдельных элементов. Во-первых, я не нашел информации о том, как реализован этот резервный механизм.
Даже если я еще не нашел решения своей настоящей проблемы, я хотел бы поделиться с вами своими знаниями о резервном механизме.
Не стесняйтесь добавлять ответы с дополнительной информацией, если я что-то пропустил;-)
2 ответа
Реализация механизма пропуска может быть найдена в FaultTolerantChunkProcessor и в RetryTemplate.
Предположим, вы настроили пропускаемые исключения, но не повторяющиеся исключения. И в текущем чанке есть неисправный элемент, вызывающий исключение.
Теперь, прежде всего, весь кусок будет написан. В процессоре write()
метод, который вы можете увидеть, что RetryTemplate
называется. Он также получает две ссылки на RetryCallback
и RecoveryCallback
,
Переключитесь на RetryTemplate
, Найдите следующий метод:
protected <T> T doExecute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState state)
Там вы можете увидеть, что RetryTemplate
повторяется до тех пор, пока он не исчерпан (т.е. ровно один раз в нашей конфигурации). Такая повторная попытка будет вызвана повторяющимся исключением. Неповторяющиеся исключения немедленно прервут механизм повтора здесь.
После того, как повторные попытки исчерпаны или прерваны, RecoveryCallback
будет называться:
e = handleRetryExhausted(recoveryCallback, context, state);
Вот где сейчас включится режим обработки одного предмета!
RecoveryCallback (который был определен в процессоре write()
метод!) установит блокировку входного блока (inputs.setBusy(true)
Запусти свой scan()
метод. Там вы можете видеть, что из куска взят один элемент:
List<O> items = Collections.singletonList(outputIterator.next());
Если этот единственный элемент может быть обработан ItemWriter
правильно, чем чанк будет закончен и ChunkOrientedTasklet
будет запускать другой кусок (для следующих отдельных элементов). Это приведет к регулярному звонку на RetryCallback
, но так как кусок был заблокирован RecoveryTemplate
, scan()
метод будет вызван немедленно:
if (!inputs.isBusy()) {
// ...
}
else {
scan(contribution, inputs, outputs, chunkMonitor);
}
Таким образом, будет обработан еще один элемент, и это будет повторяться до тех пор, пока исходный фрагмент не будет обработан элемент за элементом:
if (outputs.isEmpty()) {
inputs.setBusy(false);
Вот и все. Я надеюсь, что вы нашли это полезным. И я еще больше надеюсь, что вы могли бы легко найти это через поисковик и не тратить слишком много времени, выясняя это самостоятельно.;-)
Возможный подход к моей исходной задаче (ItemWriter хотел бы знать, работает ли он в режиме чанков или отдельных элементов), может быть одной из следующих альтернатив:
- Только когда переданный кусок имеет размер один, любые дальнейшие проверки должны быть сделаны
Когда переданный кусок является
java.util.Collections.SingletonList
мы были бы совершенно уверены, так какFaultTolerantChunkProcessor
делает следующее:Элементы списка = Collections.singletonList(outputIterator.next());
К сожалению, этот класс является закрытым, поэтому мы не можем проверить его
instanceOf
,И наоборот, если кусок является
ArrayList
мы могли бы также быть совершенно уверены, так как Весенняя партияChunk
класс использует это:private List items = new ArrayList ();
- Одно размытие слева будет буферизованными элементами, считанными из контекста выполнения. Но я ожидаю, что они также будут ArrayLists.
Во всяком случае, я все еще нахожу этот метод слишком расплывчатым. Я бы предпочел, чтобы эта информация была предоставлена структурой.
Альтернативой будет зацепить мой ItemWriter
в рамках исполнения. Может быть ItemWriteListener.onWriteError()
является целесообразным.
Обновление: onWriteError()
метод не будет вызываться, если вы работаете в режиме одного элемента и выбрасываете исключение в ItemWriter
, Я думаю, что это ошибка, поданная это: https://jira.springsource.org/browse/BATCH-2027
Так что эта альтернатива выпадает.
Вот фрагмент, чтобы сделать то же самое без каких-либо рамочных средств непосредственно в писателе
private int writeErrorCount = 0;
@Override
public void write(final List<? extends Long> items) throws Exception {
try {
writeWhatever(items);
} catch (final Exception e) {
if (this.writeErrorCount == 0) {
this.writeErrorCount = items.size();
} else {
this.writeErrorCount--;
}
throw e;
}
this.writeErrorCount--;
}
public boolean isWriterInSingleItemMode() {
return writeErrorCount != 0;
}
Внимание: здесь лучше проверить пропускаемые исключения, а не Exception
в общем.