TL; DR: Как создать Spring Batch Jobs с использованием Spring Batch Job? Сделка транзакций, по-видимому, является проблемой. Это, кажется, классический вопрос, но вот он снова:Использование весенних партий или запуск рабочих мест в задании
У меня следующий прецедент: мне нужно опросить FTP-сервер и зарегистрировать найденные XML-файлы в виде блоба в базе данных. XML имеет 0 ... N интересующих записей I необходимо отправить во внешнюю веб-службу и сохранить ответ . Ответы могут быть нерешаемыми или повторными, и мне нужно, чтобы хранит каждый запрос и их ответы для целей аудита.
Модель домена/JPA выглядит следующим образом: Batch (содержит XML blob) содержит 0-N объектов BatchRow. BatchRow содержит данные, которые должны быть отправлены в службу сервиса , а также содержит 1 ... N объектов BatchRowHistory, имеющих статус информацию о вызовах веб-сервисов.
Меня попросили реализовать это с использованием Spring Batch (Spring Integration , возможно, была другая возможность с момента интеграции). Теперь Я боролся с разными подходами, и я нахожу эту задачу намного более сложной и потому сложной, как это должно быть ИМХО.
я разделить задачи на следующие рабочие места:
Job1:
Step11: Fetch файл и хранить в базе данных в виде сгустка.
Шаг 12: Разделите XML на записи и сохраните эти записи в db.
Step13: Создать Job2 и запустить его для каждой записи, хранящейся в Шаг12. Марк Job2 создал флаг в базе данных домена для записей.
Job2:
- Step21: Вызов веб-службы для каждой записи и хранения результата в БД. Здесь повторяется повторная попытка и . Для типов Job2 необходимо перезапустить вручную.
Логикой этой структуры является то, что Job1 запускается периодически запланировано (один раз в минуту). Job2 запускается всякий раз, когда есть этих Рабочих мест, и они либо преуспели, либо их лимит повторной попытки , и они потерпели неудачу. Модель домена сохраняет в основном только результаты и Spring Batch отвечает за запуск шоу. Ручные перезагрузки и т. Д. Могут обрабатываться через Spring Batch Admin (по крайней мере, я надеюсь, что так). Также Job2 имеет идентификатор BatchRow на карте JobParameters, поэтому он может быть просмотрен в Spring Batch Admin.
Вопрос 1: Имеет ли смысл эта структура работы? I.e. создавая новые Spring Batch Jobs для каждой строки в db, похоже, что это побеждает цель и заново изобретает колесо на некотором уровне?
Вопрос 2: Как создать эти записи Job2 в шаге 13?
я получил первые проблемы с сделкой и JobRepository но удался запустить несколько рабочих мест с последующей установкой:
<batch:step id="Step13" parent="stepParent">
<batch:tasklet>
<batch:transaction-attributes propagation="NEVER"/>
<batch:chunk reader="rowsWithoutJobReader" processor="batchJobCreator" writer="itemWriter"
commit-interval="10" />
</batch:tasklet>
</batch:step>
<bean id="stepParent" class="org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean" abstract="true"/>
Пожалуйста, обратите внимание, что совершить интервал = «10» означает, что это может создать до 10 рабочих мест в настоящее время и все ... потому что batchJobCreator вызывает метод JobLauncher.run, и он идет плавно, НО itemWriter не может пишет BatchRows обратно в базу данных с обновленной информацией (boolean jobCreated flag toggled on). Очевидной причиной этого является распространение.NEVER в транзакционных атрибутах, но без этого я не могу создавать задания с помощью jobLauncher.
Поскольку обновления не передаются в базу данных, я получаю те же BatchRows снова и они загромождали бревно:
org.springframework.batch.retry.RetryException: Non-skippable exception in recoverer while processing; nested exception is org.springframework.batch.core.repository.JobExecutionAlreadyRunningException: A job execution for this job is already running: JobInstance: id=1, version=0, JobParameters=[{batchRowId=71}], Job=[foo.bar]
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor$2.recover(FaultTolerantChunkProcessor.java:278)
at org.springframework.batch.retry.support.RetryTemplate.handleRetryExhausted(RetryTemplate.java:420)
at org.springframework.batch.retry.support.RetryTemplate.doExecute(RetryTemplate.java:289)
at org.springframework.batch.retry.support.RetryTemplate.execute(RetryTemplate.java:187)
at org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:215)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.transform(FaultTolerantChunkProcessor.java:287)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:190)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:74)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:386)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:264)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:76)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:367)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:214)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:143)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:250)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:195)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:135)
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:61)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:60)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:144)
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:124)
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:135)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:293)
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:120)
at java.lang.Thread.run(Thread.java:680)
Это означает, что работа уже была создана весной Batch и он пытается снова создайте эти файлы при последующих запусках этапа 13. I может обойти эту настройку, если флаг JobCreated равен true в Job2/Step21, но для меня это похоже на неприятный случай.
Вопрос 3: У меня было больше ориентированного на объект объекта; У меня была Spring Пакетные задания для сканирования доменных таблиц с использованием довольно сложных JPQL-запросов и JPAItemReaders. Проблема с этим подходом заключается в том, что не использует более тонкие функции Spring Batch. Логика истории и повтора - это проблема . Мне нужно запрограммировать логику повтора на запросы JPQL (например, если BatchRow имеет более 3 элементов BatchRowHistory , это не сработало и необходимо вручную пересмотреть). Должен ли я укусить пулю и продолжить этот подход вместо того, чтобы пытаться установить индивидуальное Весеннее пакетное задание для каждого вызова веб-службы?
Информация о программе если необходимо: Spring Batch 2.1.9, Hibernate 4.1.2, Spring 3.1.2, Java 6.
Заранее спасибо и извините за длинный рассказ, Тимо
Edit 1: Причина, почему я думаю, что мне нужно, чтобы породить новые рабочие места это:
Loop в то время как читатель возвращает нуль или исключение
Transaction начать
читателя - процессор - цикл автора для целых N строк
конца транзакции для размера партии N
Каждой запись не удался проблема; Я хочу, чтобы вручную перезагружались исполнения (только задания перезапускаются в Spring Batch Admin, правда?) Для каждой строки в пакете, чтобы я мог использовать Spring Batch Admin для просмотра неудачных заданий (с их параметрами задания которые содержат идентификаторы строк из домена db) и перезапускают их и т. д. Как это сделать выполнить этот вид поведения без нереста и сохранить историю домена db?
Благодарим за отзыв. 1: Мне нужно сохранить исходный файл в db для аудита целей. StaxReader не может прочитать этот файл непосредственно из db afaik. 2: Да, это, кажется, проблема, которая не очень легко делается ;-) О вашей структуре: Job1 выглядит так же, как моя, кроме работы нерестовая часть. – tfriman
Работа 2 примерно такая же, когда счастливая дорожка идет, когда нет проблем с IO. Поэтому, когда вызов WS преуспевает, он пишет ответ красиво на db. Когда возникают проблемы, ничто не получает , записанный в db, потому что транзакция откатывается. Как вы позаботились о повторной попытке, если сможете написать результат WS-вызова результат в базу данных? Пропуск элемента должен быть зарегистрирован в базе данных , вам удалось это сделать? Я знаю интерфейсы RetryListener и SkipListener, но позволяют ли они записывать записи (JPA) обратно в базу данных? – tfriman