简体   繁体   English

Spring Cloud Stream - 注意并处理代理中的错误

[英]Spring Cloud Stream - notice and handle errors in broker

I am fairly new to developing distributed applications with messaging, and to Spring Cloud Stream in particular.我对开发带有消息传递的分布式应用程序相当陌生,尤其是 Spring Cloud Stream。 I am currently wondering about best practices on how to deal with errors on the broker side.我目前想知道如何处理代理端错误的最佳实践。

In our application, we need to both consume and produce messages from/to multiple sources/destinations like this:在我们的应用程序中,我们需要从多个源/目的地消费和产生消息,如下所示: 基础设施

Consumer side消费端

For consuming, we have defined multiple @Bean s of type java.util.function.Consumer .为了消费,我们定义了多个类型为@Beanjava.util.function.Consumer The configuration for those looks like this:这些配置如下所示:

spring.cloud.stream.bindings.consumeA-in-0.destination=inputA
spring.cloud.stream.bindings.consumeA-in-0.group=$Default
spring.cloud.stream.bindings.consumeB-in-0.destination=inputB
spring.cloud.stream.bindings.consumeB-in-0.group=$Default

This part works quite well - wenn starting the application, the exchanges "inputA" and "inputB" as well as the queues "inputA.$Default" and "inputB.$Default" with corresponding binding are automatically created in RabbitMQ.这部分工作得很好——当启动应用程序时,在 RabbitMQ 中自动创建了具有相应绑定的交换“inputA”和“inputB”以及队列“inputA.$Default”和“inputB.$Default”。 Also, in case of an error (eg a queue is suddenly not available), the application gets notified immediately with a QueuesNotAvailableException and continuously tries to re-establish the connection.此外,如果出现错误(例如,队列突然不可用),应用程序会立即收到QueuesNotAvailableException通知并不断尝试重新建立连接。

My only question here is: Is there some way to handle this exception in code?我唯一的问题是:有没有办法在代码中处理这个异常? Or, what are best practices to deal with failures like this on broker side?或者,在代理端处理此类故障的最佳实践是什么?

Producer side制作方

This one is more problematic.这个问题比较多。 Producing messages is triggered by some internal logic, we cannot use function @Bean s here.产生消息是由一些内部逻辑触发的,我们这里不能使用 function @Bean Instead, we currently rely on StreamBridge to send messages.相反,我们目前依靠StreamBridge来发送消息。 The problem is that this approach does not trigger creation of exchanges and queues on startup.问题是这种方法不会在启动时触发交换和队列的创建。 So when our code calls streamBridge.send("outputA", message) , the message is sent (result is true ), but it just disappears into the void since RabbitMQ automatically drops unroutable messages.因此,当我们的代码调用streamBridge.send("outputA", message)时,消息已发送(结果为true ),但由于 RabbitMQ 自动丢弃无法路由的消息,它就消失在了虚空中。

I found that with this configuration, I can at least get RabbitMQ to create exchanges and queues as soon as the first message is sent:我发现通过这种配置,我至少可以让 RabbitMQ 在发送第一条消息后立即创建交换和队列:

spring.cloud.stream.source=produceA;produceB
spring.cloud.stream.default.producer.requiredGroups=$Default
spring.cloud.stream.bindings.produceA-out-0.destination=outputA
spring.cloud.stream.bindings.produceB-out-0.destination=outputB

I need to use streamBridge.send("produceA-out-0", message) in code to make it work, which is not too great since it means having explicit configuration hardcoded, but at least it works.我需要在代码中使用streamBridge.send("produceA-out-0", message)以使其工作,这不是太好,因为这意味着对明确的配置进行硬编码,但至少它可以工作。
I also tried to implement the producer in a Reactor style as desribed in this answer , but in this case the exchange/queue also is not created on application startup and the sent message just disappears even though the return status of the sending method is "OK".我还尝试按照此答案中的描述以 Reactor 样式实现生产者,但在这种情况下,交换/队列也不会在应用程序启动时创建,即使发送方法的返回状态为“OK”,发送的消息也会消失”。

Failures on the broker side are not registered at all with this approach - when I simulate one eg by deleting the queue or the exchange, it is not registered by the application.使用这种方法根本不会注册代理端的故障 - 当我模拟一个,例如通过删除队列或交换时,应用程序不会注册它。 Only when another message is sent, I get in the logs:只有在发送另一条消息时,我才会进入日志:

ERROR 21804 --- [127.0.0.1:32404] osar.c.CachingConnectionFactory: Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'produceA-out-0' in vhost '/', class-id=60, method-id=40)

But still, the result of StreamBridge#send was true in this case.但是,在这种情况下, StreamBridge#send的结果仍然是true的。 But we need to know that sending did actually fail at this point (we persist the state of the sent object using this boolean return value).但是我们需要知道此时发送确实失败了(我们使用这个 boolean 返回值来持久化发送的 object 的 state)。 Is there any way to accomplish that?有没有办法做到这一点?

Any other suggestions on how to make this producer scenario more robust?关于如何使这个生产者场景更健壮的任何其他建议? Best practices?最佳实践?

On the consumer side, you can listen for an event such as the ListenerContainerConsumerFailedEvent .在消费者方面,您可以侦听诸如ListenerContainerConsumerFailedEvent之类的事件。

https://docs.spring.io/spring-amqp/docs/current/reference/html/#consumer-events https://docs.spring.io/spring-amqp/docs/current/reference/html/#consumer-events

On the producer side, producers only know about exchanges, not any queues bound to them;在生产者方面,生产者只知道交换,而不知道任何绑定到它们的队列; hence the requiredGroups property which causes the queue to be bound.因此导致队列被绑定的requiredGroups属性。

You only need spring.cloud.stream.default.producer.requiredGroups=$Default - you can send to arbitrary destinations using the StreamBridge and the infrastructure will be created.您只需要spring.cloud.stream.default.producer.requiredGroups=$Default - 您可以使用StreamBridge发送到任意目的地,并且将创建基础设施。

@SpringBootApplication
public class So70769305Application {

    public static void main(String[] args) {
        SpringApplication.run(So70769305Application.class, args);
    }

    @Bean
    ApplicationRunner runner(StreamBridge bridge) {
        return args -> bridge.send("foo", "test");
    }

}
spring.cloud.stream.default.producer.requiredGroups=$Default

在此处输入图像描述

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

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