简体   繁体   English

如何使用Rabbit绑定创建带有事务的处理程序和DLQ?

[英]How to create Processor with Transaction and DLQ with Rabbit binding?

I'm just starting to learn Spring Cloud Streams and Dataflow and I want to know one of important use cases for me. 我才刚刚开始学习Spring Cloud Streams和Dataflow,我想了解一个重要的用例之一。 I created example processor Multiplier which takes message and resends it 5 times to output. 我创建了示例处理器Multiplier,它接收消息并将其重新发送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));
        }
    }
}

What you can see is that before 5th sending this processor crashes. 您会看到在第5次发送此处理器之前崩溃。 Why? 为什么? Because it can (programs throw exceptions). 因为它可以(程序引发异常)。 In this case I wanted to practice fault prevention on Spring Cloud Stream. 在这种情况下,我想在Spring Cloud Stream上练习故障预防。

What I would like to achieve is to have input message backed in DLQ and 4 messages that were send before to be reverted and not consumed by next operand (just like in normal JMS transaction). 我要实现的是将输入消息备份到DLQ中,并发送4条消息,这些消息在发送之前已还原并且不被下一个操作数使用(就像在普通JMS事务中一样)。 I tried already to define following properties in my processor project but without success. 我已经尝试在处理器项目中定义以下属性,但没有成功。

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

Could you tell me if it possible and also what am I doing wrong? 你能告诉我是否可能,还有我在做什么错? I would be overwhelmingly thankful for some examples. 对于某些示例,我将非常感激。

You have several issues with your configuration: 您的配置有几个问题:

  • missing .rabbit in the rabbit-specific properties) 在兔子特定的属性中缺少.rabbit
  • you need a group name and durable subscription to use autoBindDlq 您需要组名和持久订阅才能使用autoBindDlq
  • autoBindDlq doesn't apply on the output side autoBindDlq不适用于输出端

The consumer has to be transacted so that the producer sends are performed in the same transaction. 消费者必须进行交易,以便生产者发送在同一事务中执行。

I just tested this with 1.0.2.RELEASE: 我刚刚用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

and it worked as expected. 并按预期工作。

EDIT 编辑

Actually, no, the published messages were not rolled back. 实际上,不,发布的邮件不会回滚。 Investigating... 调查...

EDIT2 EDIT2

OK; 好; it does work, but you can't use republishToDlq - because when that is enabled, the binder publishes the failed message to the DLQ and the transaction is committed. 它确实有效,但是您不能使用republishToDlq因为启用该功能后,绑定程序会将失败的消息发布到DLQ并提交事务。

When that is false, the exception is thrown to the container, the transaction is rolled back, and RabbitMQ moves the failed message to the DLQ. 如果为假,则将异常引发到容器,回滚事务,然后RabbitMQ将失败的消息移至DLQ。

Note, however, that retry is enabled by default (3 attempts) so, if your processor succeeds during retry, you will get duplicates in your output. 但是请注意,默认情况下会启用重试(3次尝试),因此,如果处理器在重试期间成功,则输出中将出现重复项。

For this to work as you want, you need to disable retry by setting the max attempts to 1 (and don't use republishToDlq ). 为了使此功能如您所愿,您需要通过将最大尝试次数设置为1来禁用重试(并且不要使用republishToDlq )。

EDIT3 EDIT3

OK, if you want more control over the publishing of the errors, this will work, when the fix for this JIRA is applied to Spring AMQP... 好的,如果您想更好地控制错误的发布,则当此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;
            }
        }

    }

}

with properties: 具有属性:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM