我希望能够使用Spring Integration Workflow一次处理10条以上的SQS消息。
从这个问题出发,建议使用 ExecutorChannel 。我更新了代码,但仍然有相同的症状。
进行此更新后,我的应用程序会请求10条消息,并对它们进行处理,只有在流程结束时我调用 amazonSQSClient.deleteMessage 后,它才会接受来自SQS的另外10条消息队列。
应用程序使用SQS FiFo队列。
还有其他我想念的东西吗?还是使用 SqsMessageDeletionPolicy.NEVER 然后在流程结束时删除消息是不可避免的症状吗?由于其他限制,在流程开始时接受消息并不是真正的选择。
以下是相关的代码片段,并进行了一些简化,但我希望它能表达问题。
队列配置
@Bean
public AsyncTaskExecutor inputChannelTaskExecutor() {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
executor.setConcurrencyLimit(50);
return executor;
}
@Bean
@Qualifier("inputChannel")
public ExecutorChannel inputChannel() {
return new ExecutorChannel(inputChannelTaskExecutor());
}
我还尝试了ThreadPoolTaskExecutor而不是SimpleAsyncTaskExecutor,结果相同,但如果它提供其他见解,我也将其包括在内。
@Bean
public AsyncTaskExecutor inputChannelTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(50);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("spring-async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.afterPropertiesSet();
executor.initialize();
return executor;
}
SQS通道适配器
@Bean
public SqsMessageDrivenChannelAdapter changeQueueMessageAdapter() {
SqsMessageDrivenChannelAdapter adapter = new SqsMessageDrivenChannelAdapter(this.amazonSQSClient, changeQueue);
adapter.setOutputChannel(inputChannel);
adapter.setMessageDeletionPolicy(SqsMessageDeletionPolicy.NEVER);
return adapter;
}
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerSpec poller() {
return Pollers.fixedRate(500, TimeUnit.MILLISECONDS).maxMessagesPerPoll(10);
}
简化的主流程
对我们来说,常见的情况是在短时间内获得许多分支编辑。此流程仅“关心”至少发生了一次编辑。 messageTransformer 从有效负载文档中提取一个ID,并将其放入标头 dsp_docId 中,然后我们将其用于汇总(我们在其他一些地方使用了该ID,因此我们觉得标头有意义,而不是在自定义聚合器中完成所有工作)。
provisioningServiceActivator 检索分支的最新版本,然后路由器决定是否需要进一步的转换(在这种情况下,它将其发送到 transformBranchChannel )或被发送到我们的PI实例(通过sendToPiChannel)。
转换流程(未显示,我认为您不需要它)最终导致发送到PI流程,它首先要做更多工作。
listingGroupProcessor 捕获所有 aws_receiptHandle 标头,并将它们作为|添加到新标头中。分隔列表。
sendToPi流(和errorFlow)以对自定义处理程序的调用结束,该处理程序负责删除aws_receiptHandle字符串列表所引用的所有SQS消息。
@Bean
IntegrationFlow sqsListener() {
return IntegrationFlows.from(inputChannel)
.transform(messageTransformer)
.aggregate(a -> a.correlationExpression("1")
.outputProcessor(listingGroupProcessor)
.autoStartup(true)
.correlationStrategy(message -> message.getHeaders().get("dsp_docId"))
.groupTimeout(messageAggregateTimeout) // currently 25s
.expireGroupsUponCompletion(true)
.sendPartialResultOnExpiry(true)
.get())
.handle(provisioningServiceActivator, "handleStandard")
.route(Branch.class, branch -> (branch.isSuppressed() == null || !branch.isSuppressed()),
routerSpec -> routerSpec.channelMapping(true, "transformBranchChannel")
.resolutionRequired(false)
.defaultOutputToParentFlow())
.channel(sendtoPiChannel)
.get();
}
答案 0 :(得分:0)
我以为我会将其发布为答案,因为这可以解决我的问题,并且可能会帮助其他人。作为答案,它更有可能被发现,而不是对可能被忽略的原始问题进行编辑。
首先,我应该注意到我们正在使用 FiFo 队列。
问题实际上是在链的更深处,我们将 MessageGroupId 设置为一个描述数据源的简单值。这意味着我们有非常大的消息组。
从ReceiveMessage文档中可以看出,在这种情况下,它很明智地停止了您从该组中请求更多消息,因为如果需要将消息放回队列中,就不可能保证订单的顺序。
更新发布消息的代码以设置适当的 MessageGroupId ,然后表示 ExecutorChannel 可以按预期工作。
虽然具有特定MessageGroupId的消息是不可见的,但是直到可见性超时到期,才不会再返回属于同一MessageGroupId的消息。只要仍然可见,您仍然可以接收带有另一个MessageGroupId的消息。