【问题标题】:How to create Processor with Transaction and DLQ with Rabbit binding?如何使用 Rabbit 绑定创建带有事务和 DLQ 的处理器?
【发布时间】:2016-08-18 12:34:40
【问题描述】:

我刚刚开始学习 Spring Cloud Streams 和 Dataflow,我想了解一个对我来说很重要的用例。我创建了示例处理器乘法器,它接收消息并将其重新发送 5 次以输出。

@EnableBinding(Processor.class)
public class MultiplierProcessor {
    @Autowired
    private Source source;

    private int repeats = 5;

    @Transactional
    @StreamListener(Processor.INPUT)
    public void handle(String payload) {
        for (int i = 0; i < repeats; i++) {
            if(i == 4) {
                throw new RuntimeException("EXCEPTION");
            }
            source.output().send(new GenericMessage<>(payload));
        }
    }
}

您可以看到,在第 5 次发送之前,该处理器崩溃了。为什么?因为它可以(程序抛出异常)。在这种情况下,我想在 Spring Cloud Stream 上练习故障预防。

我想要实现的是在 DLQ 中支持输入消息和之前发送的 4 条消息被还原并且不被下一个操作数消耗(就像在正常的 JMS 事务中一样)。我已经尝试在我的处理器项目中定义以下属性,但没有成功。

spring.cloud.stream.bindings.output.producer.autoBindDlq=true
spring.cloud.stream.bindings.output.producer.republishToDlq=true
spring.cloud.stream.bindings.output.producer.transacted=true

spring.cloud.stream.bindings.input.consumer.autoBindDlq=true

如果可能的话,你能告诉我我做错了什么吗?我会非常感谢一些例子。

【问题讨论】:

    标签: java spring rabbitmq spring-cloud-stream spring-cloud-dataflow


    【解决方案1】:

    您的配置有几个问题:

    • 兔子特定属性中缺少.rabbit)
    • 您需要组名和持久订阅才能使用autoBindDlq
    • autoBindDlq 不适用于输出端

    必须对消费者进行事务处理,以便生产者发送在同一个事务中执行。

    我刚刚用 1.0.2.RELEASE 测试了这个:

    spring.cloud.stream.bindings.output.destination=so8400out
    
    spring.cloud.stream.rabbit.bindings.output.producer.transacted=true
    
    spring.cloud.stream.bindings.input.destination=so8400in
    spring.cloud.stream.bindings.input.group=so8400
    
    spring.cloud.stream.rabbit.bindings.input.consumer.durableSubscription=true
    spring.cloud.stream.rabbit.bindings.input.consumer.autoBindDlq=true
    spring.cloud.stream.rabbit.bindings.input.consumer.transacted=true
    

    它按预期工作。

    编辑

    实际上,不,发布的消息没有回滚。正在调查...

    EDIT2

    好的;它确实有效,但您不能使用republishToDlq - 因为启用该功能后,绑定程序会将失败的消息发布到 DLQ 并提交事务。

    如果为 false,则向容器抛出异常,事务回滚,RabbitMQ 将失败消息移动到 DLQ。

    但是请注意,默认情况下会启用重试(3 次尝试),因此,如果您的处理器在重试期间成功,您将在输出中得到重复。

    为此,您需要通过将最大尝试次数设置为 1 来禁用重试(并且不要使用 republishToDlq)。

    EDIT3

    好的,如果你想更好地控制错误的发布,当this JIRA 的修复应用于 Spring AMQP...

    @SpringBootApplication
    @EnableBinding({ Processor.class, So39018400Application.Errors.class })
    public class So39018400Application {
    
        public static void main(String[] args) {
            SpringApplication.run(So39018400Application.class, args);
        }
    
        @Bean
        public Foo foo() {
            return new Foo();
        }
    
        public interface Errors {
    
            @Output("errors")
            MessageChannel errorChannel();
    
        }
    
        private static class Foo {
    
            @Autowired
            Source source;
    
            @Autowired
            Errors errors;
    
            @StreamListener(Processor.INPUT)
            public void handle (Message<byte[]> in) {
                try {
                    source.output().send(new GenericMessage<>("foo"));
                    source.output().send(new GenericMessage<>("foo"));
                    throw new RuntimeException("foo");
                }
                catch (RuntimeException e) {
                    errors.errorChannel().send(MessageBuilder.fromMessage(in)
                            .setHeader("foo", "bar") // add whatever you want, stack trace etc.
                            .build());
                    throw e;
                }
            }
    
        }
    
    }
    

    带有属性:

    spring.cloud.stream.bindings.output.destination=so8400out
    
    spring.cloud.stream.bindings.errors.destination=so8400errors
    spring.cloud.stream.rabbit.bindings.errors.producer.transacted=false
    
    
    spring.cloud.stream.rabbit.bindings.output.producer.transacted=true
    
    spring.cloud.stream.bindings.input.destination=so8400in
    spring.cloud.stream.bindings.input.group=so8400
    
    spring.cloud.stream.rabbit.bindings.input.consumer.transacted=true
    spring.cloud.stream.rabbit.bindings.input.consumer.requeue-rejected=false
    spring.cloud.stream.bindings.input.consumer.max-attempts=1
    

    【讨论】:

    • 嘿@gary-russell,谢谢你的回复。关于它的一个问题。当我将此处理器与 Dataflow 一起使用时,是否需要设置 output.destination?
    • 查看我的第二次编辑了解更多信息。如果您不设置目的地,则消息将发布到名为output 的交换器,路由键为output。
    • 对不起 - 你说“使用数据流” - 不 - 数据流负责命名。
    • 非常感谢,这确实有效 :) 但是你能告诉我为什么我不能使用 republishToDlq 吗?获得有关所发生事件的更多信息将对调查非常有帮助。
    • 由 binder 重新发布被认为是故障的“恢复”,因此事务被提交(包括在这种情况下发送的输出)。我删除了我之前的评论(关于在您的应用程序中重新发布),因为这不起作用 - 重新发布仍然在同一个事务中运行。我需要在 Spring AMQP 中做一些工作来避免这种情况。我开了一个JIRA Issue。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-31
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多