Java Spring集成在下一次集成之前完成异步转换
我有一个集成流程,它定期轮询数据库,以检索任何尚未处理的Java Spring集成在下一次集成之前完成异步转换,java,jpa,asynchronous,spring-integration,spring-integration-dsl,Java,Jpa,Asynchronous,Spring Integration,Spring Integration Dsl,我有一个集成流程,它定期轮询数据库,以检索任何尚未处理的MachineLine实体并对其进行处理。流检索MachineLine对象的集合,然后我希望将其拆分为单个对象,将这些对象转换为ReportDetails对象,并将转换后的对象持久化到数据库中的另一个表中。流量定义如下: @Bean public IntegrationFlow processMachineLine() { return IntegrationFlows .from(Jpa.inboundAd
MachineLine
实体并对其进行处理。流检索MachineLine
对象的集合,然后我希望将其拆分为单个对象,将这些对象转换为ReportDetails
对象,并将转换后的对象持久化到数据库中的另一个表中。流量定义如下:
@Bean
public IntegrationFlow processMachineLine() {
return IntegrationFlows
.from(Jpa.inboundAdapter(this.entityManager)
.entityClass(MachineLine.class)
.jpaQuery(this.machineService.retrieveUnprocessedLinesQuery()),
e -> e.poller(Pollers.fixedDelay(5000)))
.split()
.transform(MachineLine.class, this::transformMachineLineToReportDetails)
.handle(Jpa.outboundAdapter(this.entityManager)
.entityClass(ReportDetails.class),
ConsumerEndpointSpec::transactional)
.get();
}
上面的定义很好,但速度很慢。transformMachineNetoReportDetails
方法向另一个服务发送HTTP请求,该请求需要几秒钟才能响应。对于当前流定义,每个MachineLine
对象在对其执行相同操作之前,都会等待前一个对象被转换和持久化
因此,理想的解决方案是异步执行此转换和持久性。一种可能的解决方案是在.split()
和.transform(…)
之间插入以下行:
.channel(新执行器通道(Executors.newCachedThreadPool())
但是,这允许JPA入站适配器在处理和持久化最后一次轮询的结果之前再次轮询数据库。这意味着前一次数据库轮询返回的、在下一次轮询之前未被转换和持久化的任何MachineLine
实体将被再次检索,并尝试进行第二次转换和持久化。这显然会导致不必要的资源开销,并且在多个具有相同ID的ReportDetails
对象试图持久化到数据库时也会产生错误
是否有一种方法可以异步转换
MachineLine
对象,但确保在上一次轮询的结果通过流完成之前不会再次轮询数据库(即,所有MachineLine
对象都被转换并持久化)?我通过自定义AbstractMessageSourceAdvice
查看它的唯一方法是针对一些原子布尔
标志(也可以是bean)签入beforeceive()
。由于您使用的是轮询器.fixedDelay(5000)
,因此您的轮询策略仍然是单线程的。因此,当AbstractMessageSourceAdvice
不允许对JPA执行轮询时,我们最好不要让同一线程对JPA执行轮询。在begging中,布尔标志应该是true
,并且在提到的split()
之前将其更改为false
。您可以将publishSubscribeChannel()
作为两个订阅者使用。甚至在AbstractMessageSourceAdvice
实现中也可以这样做,有点像compareAndSet(true,false)
中的beforeReceive()
实现
然后在转换后使用ExecutorChannel
进行拆分和持久化
在流的末尾,您需要放置一个带有两个订阅者的publishSubscribeChannel()
——1<代码>句柄(Jpa.outboundAdapter(this.entityManager);2.aggregate()
等待所有拆分的项目完成。然后aggregate()
放置一个简单的句柄(m->pollingFlagBean().set(true))
仅此而已:只有当所有项目都被处理并聚合回组时,才会发生新的轮询。只有在这之后,您才允许轮询器使用该AtomicBoolean
再次进行轮询
您还可以考虑将此标志逻辑与<>代码> SimeActive IdMeLeSaveSuthCeope更改活动模式和被动模式之间的轮询周期,以避免在等待聚合时大空闲。
任何其他异步解决方案仍然无法为您工作,因为切换到其他线程将立即释放轮询进程,让它再次旋转。感谢您的回复,Artem!这是一个非常有用的答案。此外,感谢您和团队其他成员在Spring集成方面所做的出色工作。项目太棒了!