Spring boot 如何设计一个Springbatch作业,它可以批量从数据库中获取记录,并并行运行多个处理器和写入程序
场景:从数据库读取记录并从中创建4个不同的输出文件 技术堆栈:Spring boot 如何设计一个Springbatch作业,它可以批量从数据库中获取记录,并并行运行多个处理器和写入程序,spring-boot,architecture,spring-batch,jobs,Spring Boot,Architecture,Spring Batch,Jobs,场景:从数据库读取记录并从中创建4个不同的输出文件 技术堆栈: Springboot 2.x springBatch 4.2.x ArangoDB 3.6.x 当前方法:SpringBatch作业,按顺序包含以下步骤: jobBuilderFactory.get("alljobs") .start(step("readAllData")) //reads all records from db, stores it in Obj1 (R1) .next(step("p
- Springboot 2.x
- springBatch 4.2.x
- ArangoDB 3.6.x
jobBuilderFactory.get("alljobs")
.start(step("readAllData")) //reads all records from db, stores it in Obj1 (R1)
.next(step("processData1")) //(P1)
.next(step("writer1")) // writes it to file1(W1)
.next(step("reader2")) // reads the same obj1(R2)
.next(step("processor2")) // processes it (P2)
.next(step("writer2")) // writes it to file1(W2)
.next(step("reader3")) // reads the same obj1 (R3)
.next(step("processor3")) // processes it (P3)
.next(step("writer3")) // writes it to file1(W3)
.next(step("reader4")) // reads the same obj1(R4)
.next(step("processor4")) // processes it (P4)
.next(step("writer4")) // writes it to file1 (W4)
.build()
问题:由于来自DB的数据量巨大,>200000条记录,因此现在我们通过游标在10000条记录中获取记录
作业的目标状态:一个读卡器作业,它通过一个游标从数据库中批量获取1000条记录:
Obj1
,将从光标中获取),从而并行触发它们- 在游标本身内调用作业,并在游标内逐个迭代执行所有步骤
P1…W4
- 调用第一步为
的作业,然后在游标内调用另一个Reader1
,该作业同时具有所有这些subJob
,因为我们不能离开游标P1…W4
Flow
):
但是我不能返回到下一个游标迭代,因为游标迭代只在R1步骤中。
我还尝试将所有步骤R1…W4(包括Reader1)放在同一个流中,但该流最终抛出循环流错误
请提出更好的实施方法?如何在R1步骤迭代的游标内并行调用所有其他步骤。我相信使用4个并行步骤对您来说是一个不错的选择。即使有4个线程从相同的数据中读取数据,也应该从处理/写入阶段的并行步骤中获益。这肯定比按顺序执行4个步骤要好。顺便说一句,200k记录并没有那么多(当然这取决于记录的大小和映射方式,但我认为这应该是可以的,读取数据从来不是瓶颈) 总是要权衡取舍。。在这里,由于并行的步骤,我用一点读取复制来换取更好的总体吞吐量。我不会自杀,以确保项目只读一次,并使事情复杂化
在数据库世界中,这种权衡的一个很好的类比是接受一些数据复制以支持更快的查询(想想NoSQL设计,有时建议复制一些数据以避免昂贵的连接)。这就是我最终设计解决方案的方式: 因此,我重新构建了整个流程,从基于Tasklet的方法到基于编排块的方法 作业将有一个名为-fetchProcessAndWriteData的步骤
jobBuilderFactory.get("allChunkJob")
.start(step("fetchProcessAndWriteData"))
.next(step("updatePostJobRunDetails"))
.build()
fetchProcessAndWriteData:将具有块大小为10000的读卡器、主处理器和主写入器
steps
.get("fetchProcessAndWriteData")
.chunk(BATCHSIZE)
.reader(chunkReader)
.processecor(masterProcessor)
.writer(masterWriter)
.listener(listener())
.build()
chunkReader-从数据库光标读取数据块,并将其传递给主处理器
public Object process(Object item){
..
bean.setRecordType1(P1.process(item));
bean.setRecordType2(P2.process(item));
bean.setRecordType3(P3.process(item));
bean.setRecordType4(P4.process(item));
return bean;
}
主处理器逐个接受数据,并将记录传递给所有其他处理器C-P1、P2、P3、P4
并将处理后的数据存储在compositeResultBean中
CompositeResultBean包含所有4种类型记录的数据持有者
List<Record> recordType1.
List<Record> recordType2.
List<Record> recordType3.
List<Record> recordType4.
masterWriter-此步骤接受记录列表,即此处的compositeResultBean列表。迭代bean列表并调用相应的
writers W1、W2、W3、W4 writer()方法,其中每个compositeResultBean属性中都保存有数据
公共无效写入(列表){
这些整个步骤在一批10k记录中执行,并将数据写入文件
在编写文件的过程中,我面临的另一个挑战是,在第一次写入记录时,我必须替换已经存在的文件,但在同一个文件中必须附加后续文件。
我解决了这个问题,在masterWriter中对chunkListener进行了超调,在那里我拉入了批#,并将静态标志isInitialBatch默认设置为TRUE。
此变量设置在
beforeChunk()
if chunkContext.getStepContext().getStepExecution().getCommitCount()==0 as TRUE , else FALSE .
在FileWriter中传递相同的布尔值,FileWriter以append-TRUE或FALSE模式打开文件
W1.write(isInitialBatch,list.getRecordType1());
感谢@Mahmoud的输入。但是,即使所有4个处理器使用相同的obj。在所有4种情况下,要写入的对象都不同。例如,P1读取相同的obj1。但创建objP1,然后由W1写入。P2读取相同的obj1,但创建objP2,然后由W2写入。因此,在这种情况下,我的4个处理器在nat中都不同好吧,这不是我从你的例子中理解的:
b)W1(Obj1);e)W2(Obj1);| e)W3(Obj1);| e)W4(Obj1);
。对我来说,这意味着要将同一个对象写入不同的目的地4次,因此我建议使用复合编写器。我将相应地编辑答案。感谢@Muhamoud提供更新的答案,这很有帮助。我试图实现类似的逻辑,但面临相同的问题。我一直在研究如何实现决策器和流这里有问题,所以需要一些帮助。我已经用我正在尝试的当前逻辑更新了我的问题。在springboot中有没有其他方法/概念可以让我使用?在这种情况下,请接受答案:。对于第二个问题,请问一个单独的问题
list.forEach(record -> {
W1.write(isInitialBatch,list.getRecordType1());
W2.write(isInitialBatch,list.getRecordType2());
W3.write(isInitialBatch,list.getRecordType3());
W4.write(isInitialBatch,list.getRecordType4());
});
}
beforeChunk()
if chunkContext.getStepContext().getStepExecution().getCommitCount()==0 as TRUE , else FALSE .
W1.write(isInitialBatch,list.getRecordType1());