消息移交给QueueChannel后如何处理错误?

时间:2019-01-30 11:46:22

标签: spring-integration spring-integration-amqp

我有10个RabbitMQ队列,分别称为event.q.0,event.q.2,<...>,event.q.9。这些队列中的每个队列都接收从event.consistent-hash交换路由的消息。我想构建一个容错解决方案,该解决方案将按顺序使用特定事件的消息,因为顺序很重要。为此,我建立了一个侦听这些队列并将基于事件ID的消息路由到特定工作流的流。工作流基于队列通道进行工作,因此应确保具有特定ID的事件的FIFO顺序。我提出了以下设置:

@Bean
public IntegrationFlow eventConsumerFlow(RabbitTemplate rabbitTemplate, Advice retryAdvice) {
    return IntegrationFlows
            .from(
                    Amqp.inboundAdapter(new SimpleMessageListenerContainer(rabbitTemplate.getConnectionFactory()))
                            .configureContainer(c -> c
                                    .adviceChain(retryAdvice())
                                    .addQueueNames(queueNames)
                                    .prefetchCount(amqpProperties.getPreMatch().getDefinition().getQueues().getEvent().getPrefetch())
                            )
                            .messageConverter(rabbitTemplate.getMessageConverter())
            )
            .<Event, String>route(e -> String.format("worker-input-%d", e.getId() % numberOfWorkers))
            .get();
}

private Advice deadLetterAdvice() {
    return RetryInterceptorBuilder
            .stateless()
            .maxAttempts(3)
            .recoverer(recoverer())
            .backOffPolicy(backOffPolicy())
            .build();
}

private ExponentialBackOffPolicy backOffPolicy() {
    ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
    backOffPolicy.setInitialInterval(1000);
    backOffPolicy.setMultiplier(3.0);
    backOffPolicy.setMaxInterval(15000);

    return backOffPolicy;
}

private MessageRecoverer recoverer() {
    return new RepublishMessageRecoverer(
            rabbitTemplate,
            "error.exchange.dlx"
    );
}

@PostConstruct
public void init() {
    for (int i = 0; i < numberOfWorkers; i++) {
        flowContext.registration(workerFlow(MessageChannels.queue(String.format("worker-input-%d", i), queueCapacity).get()))
                .autoStartup(false)
                .id(String.format("worker-flow-%d", i))
                .register();
    }
}

private IntegrationFlow workerFlow(QueueChannel channel) {
    return IntegrationFlows
        .from(channel)
        .<Object, Class<?>>route(Object::getClass, m -> m
                .resolutionRequired(true)
                .defaultOutputToParentFlow()
                .subFlowMapping(EventOne.class, s -> s.handle(oneHandler))
                .subFlowMapping(EventTwo.class, s -> s.handle(anotherHandler))
        )
        .get();
}

现在,当说eventConsumerFlow中发生错误时,重试机制按预期工作,但是当workerFlow中发生错误时,重试不再起作用并且消息不再起作用被发送到死信交换。我认为这是因为一旦消息传递到QueueChannel,它就会自动得到确认。我如何使重试机制也能在workerFlow中工作,以便在那里发生异常,它可以重试几次,并在尝试用尽时向DLX发送消息?

1 个答案:

答案 0 :(得分:0)

如果您要具有弹性,则根本不应该使用队列通道。将邮件放入内存队列后,将立即确认该邮件;如果服务器崩溃,这些邮件将丢失。

如果不希望丢失消息,则应该为每个队列配置一个单独的适配器。

也就是说,要回答一般性问题,下游流(包括队列通道之后)上的任何错误都将发送到入站适配器上定义的errorChannel