TL; DR :如何使用Spring Batch Job创建Spring Batch Jobs? 交易边界似乎是问题所在。这似乎是一个 经典问题,但又在这里:
我有以下用例:我需要轮询FTP服务器并找到存储 XML文件作为数据库中的blob。 XML有0 ... N个感兴趣的条目我 需要发送到外部Web服务并存储 响应。响应可以是不可重试的或可重试的,我需要 存储每个请求及其响应以用于审计目的。
域/ JPA模型如下:Batch(包含XML blob)包含 0-N BatchRow对象。 BatchRow包含要发送到Web的数据 服务,它还包含1 ... N个BatchRowHistory对象保持状态 有关Web服务调用的信息。
我被要求使用Spring Batch(Spring Integration)来实现它 从这种整合的情况来看,可能还有其他可能性)。现在 我已经在不同的方法上挣扎,我发现这个任务很多 因为恕我直言,应该更加复杂,因此也很困难。
我已将任务拆分为以下作业:
作业1 :
步骤11:获取文件并以blob形式存储到数据库中。
步骤12:将XML拆分为条目并将这些条目存储到db。
Step13 :创建Job2并为存储的每个条目启动它 步骤12。 Mark Job2在域模型中创建了标记 条目数据库。
作业2 :
这种结构背后的逻辑是Job1定期运行 预定(每分钟一次)。只要有,就运行Job2 那些乔布斯和他们已经成功或他们的重试限制上升 他们失败了。域模型基本上只存储结果和 Spring Batch负责运行show。手动重启 可以通过Spring Batch Admin处理(至少我希望如此)。也 Job2在JobParameters地图中具有BatchRow的ID,因此它可以 在Spring Batch Admin中查看。
问题1 :这种工作结构是否有意义? I.e.创造新的 对于db中的每一行的Spring Batch Jobs,它有点似乎打败了 目的并在某种程度上重新发明轮子?
问题2 :如何在Step13中创建这些Job2条目?
我遇到了事务和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"/>
请注意,commit-interval =&#34; 10&#34;意味着这可以创造多达10个 目前的工作和那个工作......因为batchJobCreator调用 JobLauncher.run方法,它游泳但但itemWriter不能 使用更新的信息将BatchRows写回数据库(布尔值 jobCreated标志切换为)。明显的原因是在交易属性中传播.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)
这意味着已经在Spring Batch中创建了作业 尝试在以后执行Step13时再次创建这些文件。一世 可以绕过这个设置,将jobCreated标志设置为true Job2 / Step21,但对我来说这感觉很糟糕。
问题3 :我有更多的域对象驱动方法;我有春天 批处理作业使用相当精细的JPQL查询扫描域表 和JPAItemReaders。这种方法的问题在于这样做 不使用Spring Batch的更好的功能。历史和重试逻辑是 问题。我需要将重试逻辑编码为JPQL查询 直接(例如,如果BatchRow具有多于3个BatchRowHistory 它失败的元素,需要手动重新检查)。我应该 咬紧牙关继续这种方法,而不是试图 为每个Web服务调用创建单独的Spring Batch作业?
软件信息,如果需要:Spring Batch 2.1.9,Hibernate 4.1.2,Spring 3.1.2,Java 6。
提前感谢你,并且为长篇小说感到抱歉,蒂莫
编辑1: 我认为我需要产生新工作的原因是:
循环播放器返回null或抛出异常
交易开始
读取器 - 处理器 - 整个N行的编写器循环
批量大小N的交易结束
每个失败的条目都是问题;我想手动重启 执行(作业是唯一可以在Spring中重启的 批量管理,对吧?)批处理中的每一行,以便我可以使用 Spring Batch Admin查看失败的作业(带有作业参数) 其中包含来自域db的行ID)并重新启动那些等。我该怎么做 在不产生作业和存储的情况下完成这种行为 域db的历史记录?
答案 0 :(得分:0)
好的,我讨厌回答问题......但我需要了解一些事情?
1)如果您的输入文件是XML,为什么不在它们上使用StaxEventItemReader并只是在步骤1中保留您的条目?
2)从一个步骤开始第二个工作!!!!我甚至不知道它是否应该起作用......但IMO ......它闻起来;-)
为什么不定义另一个使用JdbcCursorItemReader读取条目并在ItemProcessor中调用Web服务,然后将结果写入数据库的步骤?
也许我不理解您为每次调用Web服务创建不同作业的要求!
我做了类似于您的用例的事情,并且使用了这个场景:
工作1: 步骤1:读取xml,处理pojo-&gt;域obj,在DB中写入域obj
工作2: 步骤1:从db读取obj,process =调用WS,在DB中写入响应
这很简单,效果很好(包括可重启和跳过功能)
希望它会有所帮助
问候