我有一个配置的spring集成管道,其中xml文件被解析为各种对象。这些对象经过几个通道端点,稍微修改它们 - 没什么特别的,只添加了一些属性。
管道中的最后一个端点是persister,其中对象持久保存在DB中。可能存在重复项,因此在此端点中还会检查对象是已保留还是新对象。 我使用消息驱动的架构,使用简单的直接通道。
<int:channel id="parsedObjects1" />
<int:channel id="parsedObjects2" />
<int:channel id="processedObjects" />
<int:service-activator input-channel="parsedObjects1" ref="processor1" method="process" />
<int:service-activator input-channel="parsedObjects2" ref="processor2" method="process" />
<int:service-activator input-channel="processedObjects" ref="persister" method="persist" />
目前只有一个数据源,我从哪里获得xml文件,一切顺利。当我需要附加第二个数据源时,问题就开始了。这些文件是同时出现的,所以我希望它们并行处理。所以,我已经放置了两个解析器实例,每个解析器都通过管道发送消息。 我有直接通道的配置会产生并发问题,所以我尝试修改它。我从spring集成文档中尝试了几种配置,但到目前为止还没有成功。
我尝试使用配置了最大池大小为1的调度程序 - 每个通道端点中每条消息一个线程。
<task:executor id="channelTaskExecutor" pool-size="1-1" keep-alive="10" rejection-policy="CALLER_RUNS" queue-capacity="1" />
<int:channel id="parsedObjects1" >
<int:dispatcher task-executor="channelTaskExecutor" />
</int:channel>
<int:channel id="parsedObjects2" >
<int:dispatcher task-executor="channelTaskExecutor" />
</int:channel>
<int:channel id="processedObjects" >
<int:dispatcher task-executor="channelTaskExecutor" />
</int:channel>
我也尝试过queue-poller配置:
<task:executor id="channelTaskExecutor" pool-size="1-1" keep-alive="10" rejection-policy="CALLER_RUNS" queue-capacity="1" />
<int:channel id="parsedObjects1" >
<int:rendezvous-queue/>
</int:channel>
<int:channel id="parsedObjects2" >
<int:rendezvous-queue/>
</int:channel>
<int:channel id="processedObjects" >
<int:rendezvous-queue/>
</int:channel>
<int:service-activator input-channel="parsedObjects1" ref="processor1" method="process" >
<int:poller task-executor="channelTaskExecutor" max-messages-per-poll="1" fixed-rate="2" />
</int:service-activator>
<int:service-activator input-channel="parsedObjects2" ref="processor2" method="process" >
<int:poller task-executor="channelTaskExecutor" max-messages-per-poll="1" fixed-rate="2" />
</int:service-activator>
<int:service-activator input-channel="processedObjects" ref="persister" method="persist" >
<int:poller task-executor="channelTaskExecutor" max-messages-per-poll="1" fixed-rate="2" />
</int:service-activator>
基本上,我想摆脱通道端点中的任何竞争条件 - 在我的情况下是在persister中。持久性通道端点应该阻塞每条消息,因为如果它并行运行,我会在数据库中保留许多重复项。
编辑:
经过一些调试我已经完成了,似乎问题出在端点逻辑而不是配置中。通过管道发送到持久性的一些对象也存储在本地缓存中,直到完成文件的解析 - 它们稍后通过管道发送,以便将某些连接表作为其他域的一部分保留实体。碰巧使用上面的配置,当一些对象在管道中第二次发送时,它们还没有被持久化,所以最后我在数据库中得到了重复。 我在春季整合方面相当新,所以在这一点上我可能会提出更多一般性问题。在具有多个数据源的设置中 - 意味着解析器的多个实例等:
欢迎任何建议。提前谢谢。
答案 0 :(得分:1)
首先,您能描述一下“并发性问题”是什么吗?理想情况下,您不需要序列化消息处理,因此这将是一个很好的起点。
其次,您配置的线程池不会完全序列化。您将在池中有<1>个 ,但是您选择的拒绝策略会导致调用程序线程在队列处于容量状态时运行任务本身(基本上是限制)。这意味着你将从池中获得一个调用者运行的线程。
答案 1 :(得分:0)
我能为你的场景考虑的最佳方式是:
使您的parsedObject1和parsedObject2成为正常的队列通道,可以适当地设置队列的容量(比如任何时候25):
<int:channel id="parsedObjects1" >
<int:queue />
</int:channel>
现在,此时针对2个通道的xml处理器 - parsedObjects1和parsedObjects2将处理xml,并应输出到processedObjects
通道。你可以使用类似于你的配置,除了我已经明确指定了processedObjects通道 - :
<int:service-activator input-channel="parsedObjects1" ref="processor1" method="process" output-channel="processedObjects">
<int:poller task-executor="channelTaskExecutor"/>
</int:service-activator>
第三步是我将偏离您的配置,此时您说要序列化持久性,最好的方法是通过池大小为1的DIFFERENT任务执行器来执行此操作,这种方式只有您的persister的1个实例在任何时间点都在运行:
<task:executor id="persisterpool" pool-size="1"/>
<int:service-activator input-channel="processedObjects" ref="persister" method="persist" >
<int:poller task-executor="persisterpool" fixed-delay="2"/>
</int:service-activator>
答案 2 :(得分:0)
我设法让管道工作。我不确定我是否会保留当前配置,或者进行更多实验,但就目前来说,这是我最终得到的配置:
<task:executor id="channelTaskExecutor" pool-size="1-1" keep-alive="10" rejection-policy="CALLER_RUNS" queue-capacity="1" />
<int:channel id="parsedObjects1" >
<int:queue capacity="1000" />
</int:channel>
<int:channel id="parsedObjects2" >
<int:queue capacity="1000" />
</int:channel>
<int:channel id="processedObjects" >
<int:queue capacity="1000" />
</int:channel>
<int:service-activator input-channel="parsedObjects1" ref="processor1" method="process" >
<int:poller task-executor="channelTaskExecutor" max-messages-per-poll="100" fixed-rate="2" />
</int:service-activator>
<int:service-activator input-channel="parsedObjects2" ref="processor2" method="process" >
<int:poller task-executor="channelTaskExecutor" max-messages-per-poll="100" fixed-rate="2" />
</int:service-activator>
<int:service-activator input-channel="processedObjects" ref="persister" method="persist" >
<int:poller task-executor="channelTaskExecutor" max-messages-per-poll="1" fixed-rate="2" />
</int:service-activator>