Spring AMQP和Elasticsearch - 聚合消息

时间:2014-10-07 08:35:14

标签: elasticsearch rabbitmq aggregate spring-integration spring-amqp

我们在某些RabbitMQ队列上有一个使用者,它读取消息,然后在Elasticsearch中对这些数据编制索引。使用spring-amqp完成实现。为了提高性能,我们计划在消费者级别聚合消息,并在Elasticsearch中进行批量插入(这样可以提高性能)。

你对如何实现这个有什么消息吗?另外,另一个敏感问题是如何处理响应。每条消息都有一个" reply_to"我们使用带有回复通道的入站网关,因此对于每条消息都应该传递一个响应。

我正在考虑使用Spring集成中的聚合器,以及基于批量大小和MessageGroupStore过期时间(当然还有收割者)的发布策略。入站网关的任务执行程序为20,并且预取计数也为20。每当请求到来时,该消息将被添加到组存储中,并且当canRelease()条件正常时,该请求附带的其中一个线程的收割者将执行批量操作。但是我对其他线程做了什么,它将不得不等待永远不会发生的响应。此外,我不知道如何打破大型聚合消息的响应,因此每个小请求都会有响应。

另一个问题,我该如何确认消息?从我读到的事务中,会降低RabbitMQ方面的性能,所以我对使用" tx-size"属性。如果超时太小,此属性也可能执行错误计数。

1 个答案:

答案 0 :(得分:1)

关于消费者和聚合者的问题的答案:

使用来自AMQP和聚合的消息的配置。 聚合策略基于Transction提交:

<amqp:inbound-channel-adapter queue-names="myQueue"
                                  transaction-manager="transactionManager"
                                  channel-transacted="true"
                                  channel="aggregateChannel"
                                  advice-chain="aggregatorReaperAdvice"
                                  concurrent-consumers="4"
                                  tx-size="100"/>

<aggregator input-channel="aggregateChannel" output-channel="storeChannel"
                expire-groups-upon-completion="true"
                correlation-strategy-expression="T(Thread).currentThread().id"
                release-strategy-expression="^[payload.equals(@AGGREGATOR_RELEASE_MARK)] != null"
                expression="?[!payload.equals(@AGGREGATOR_RELEASE_MARK)].![payload]"/>

ReaperAdvice(Groovy代码):

@Service
class AggregatorReaperAdvice implements MethodBeforeAdvice, InitializingBean {

    private static final TRANSACTION_RESOURCE_MARK = 'TRANSACTION_RESOURCE_MARK'

    public static final AGGREGATOR_RELEASE_MARK = 'AGGREGATOR_RELEASE_MARK'

    MessagingTemplate messagingTemplate

    @Autowired
    MessageChannel aggregateChannel

    @Override
    void afterPropertiesSet() throws Exception {
        Assert.notNull aggregateChannel, "aggregateChannel must not be null"
        messagingTemplate = new MessagingTemplate(aggregateChannel)
    }

    @Override
    void before(Method method, Object[] args, Object target) {
        if (!TransactionSynchronizationManager.hasResource(AggregatorReaperAdvice)) {
            TransactionSynchronizationManager.bindResource(AggregatorReaperAdvice, TRANSACTION_RESOURCE_MARK)
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {

                @Override
                void beforeCommit(boolean readOnly) {
                    messagingTemplate.send(MessageBuilder.withPayload(AGGREGATOR_RELEASE_MARK).build())
                }

                @Override
                void afterCompletion(int status) {
                    TransactionSynchronizationManager.unbindResource(AggregatorReaperAdvice)
                }

            })
        }
    }
}

如果不清楚,请告诉我。

所有其他问题,很快就会得到解决。

对于manual ack,您可以在channel.basicAck(deliveryTag, true);上使用ack - deliveryTag用于所有之前的消息。

对于headers["reply_to"]案例......我认为您应该为AbstractAggregatingMessageGroupProcessor提供自定义aggregator并杀死两只鸟:聚合器的累积结果和MessageGroup.getMessages()上的迭代将每个人的回复流程发送到提供的MessageChannel。它是您案例的快速解决方案。

类似但松散耦合的解决方案可能基于聚合器及其MessageGroupStore的结果,您可以在其中提取correlationKey以检索组及其消息以执行所需的reply逻辑。在这种情况下,您不应该使用聚合器从商店中删除组,而是在该组检索后手动删除。