简体   繁体   English

Spring Cloud 函数和死信交换

[英]Spring Cloud Function and Dead Letter Exchange

I am trying to write a reactive Spring Cloud Function service using RabbitMQ which will consume off one queue and produce to an exchange.我正在尝试使用 RabbitMQ 编写一个反应式 Spring Cloud Function 服务,它将消耗一个队列并生成一个交换。

I have 2 questions.我有2个问题。

  1. Why am I getting the error below in the logs.为什么我在日志中收到以下错误。
  2. How would I do a reject with a doOnError?我将如何使用 doOnError 拒绝? The doOnError has access to only the throwable, and not the message to do a reject. doOnError 只能访问 throwable,而不能访问执行拒绝的消息。

Here is the application code.这是应用程序代码。 It is copied from this question Spring Reactive Stream - Unexpected Shutdown它是从这个问题Spring Reactive Stream - Unexpected Shutdown复制而来的

@SpringBootApplication
public class StreamApplication {

  public static void main(String[] args) {
    SpringApplication.runStreamApplication args);
  }

  @Bean
  public Function<Flux<Message<String>>, Flux<Message<String>>> transform() {

    return inbound -> inbound.map(msg -> {
      try {
        System.out.println("ACKING MESSAGE");
        Channel channel = msg.getHeaders().get(AmqpHeaders.CHANNEL, Channel.class);
        channel.basicAck(msg.getHeaders().get(AmqpHeaders.DELIVERY_TAG, Long.class), false);
      }
      catch (IOException e) {
        e.printStackTrace();
      }
      return msg;
    });
  }
}

Here is the application.yml.这是 application.yml。 It has 2 different binders for the incoming from queue, and outgoing to exchange.它有 2 个不同的绑定器用于从队列传入和传出到交换。

spring:
  cloud:
    stream:
      override-cloud-connectors: true
      rabbit:
        bindings:
          events-processor:
            producer:
              bindQueue: false
              declareExchange: false
              routing-key-expression: headers.eventType
          events:
            consumer:
              acknowledge-mode: MANUAL   
              prefetch: 10
              #auto-bind-dlq: false
              dead-letter-exchange: dead-letter-exchange
              bindQueue: false
              declareExchange: false
      function:
        definition: transform
        bindings:
          transform-in-0: events
          transform-out-0: events-processor
      bindings:
        events:
          destination: queue
          binder: consumerrabbit
          group: events-processor
        events-processor:
          destination: activity-events
          binder: producerrabbit
      binders:
        producerrabbit:
          defaultCandidate: false
          inheritEnvironment: false
          type: rabbit
          environment:
            spring:
              rabbitmq:
                host: host
                port: port
                username: username
                password: password
                virtual-host: virtual-host
        consumerrabbit:
          defaultCandidate: true
          inheritEnvironment: false
          type: rabbit
          environment:
            spring:
              rabbitmq:
                host: host
                port: port
                username: username
                password: password
                virtual-host: virtual-host

Here are the logs for the startup and for when the application receives an event.以下是启动和应用程序接收事件时的日志。 I am not sure why during startup the channel has a subscriber, but as soon as it gets a message it says it has no subscribers.我不确定为什么在启动期间频道有订阅者,但是一旦收到消息,它就会说它没有订阅者。

2020-09-07 12:02:04.076  INFO 10652 --- [           main] o.s.c.s.m.DirectWithAttributesChannel    : Channel 'application-1.events' has 1 subscriber(s).
2020-09-07 18:02:08.848  INFO 10652 --- [           main] o.s.c.s.m.DirectWithAttributesChannel    : Channel 'application-1.events-processor' has 1 subscriber(s).
2020-09-07 12:02:05.081  INFO 10652 --- [           main] o.s.i.monitor.IntegrationMBeanExporter   : Registering MessageChannel events
2020-09-07 12:02:05.145  INFO 10652 --- [           main] o.s.i.monitor.IntegrationMBeanExporter   : Registering MessageChannel errorChannel
2020-09-07 12:02:05.178  INFO 10652 --- [           main] o.s.i.monitor.IntegrationMBeanExporter   : Registering MessageChannel events-processor
...................other startup log messages...............
2020-09-07 18:05:09.896  INFO 10652 --- [nts-processor-1] o.s.c.s.m.DirectWithAttributesChannel    : Channel 'application-1.events' has 0 subscriber(s).
ACKING MESSAGE
2020-09-07 18:05:12.900 ERROR 10652 --- [nts-processor-1] o.s.integration.handler.LoggingHandler   : org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application-1.events'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload={
  "data": {
  
  }
}, headers={amqp_receivedDeliveryMode=NON_PERSISTENT, amqp_receivedExchange=source, amqp_deliveryTag=1, deliveryAttempt=3, amqp_consumerQueue=source.events-processor, amqp_channel=Cached Rabbit Channel: AMQChannel(amqp:), conn: Proxy@43e869ea Shared Rabbit Connection: SimpleConnection@213451a4 [delegate=amqp:], amqp_redelivered=false, id=35cdfa7f-7f75-5502-aede-6ec9569145f0, amqp_consumerTag=amq.ctag-Z87p6nKN4PLJKDbZQpXnVw, sourceData=(Body:'[B@20dc391b(byte[401])' MessageProperties [headers={}, contentLength=0, receivedDeliveryMode=NON_PERSISTENT, redelivered=false, receivedExchange=source, receivedRoutingKey=, deliveryTag=1, consumerTag=amq.ctag-Z87p6nKN4PLJKDbZQpXnVw, consumerQueue=source.events-processor]), contentType=application/json, timestamp=1599501912897}], failedMessage=GenericMessage [payload={
  "data": {
  
  }
}, headers={amqp_receivedDeliveryMode=NON_PERSISTENT, amqp_receivedExchange=source, amqp_deliveryTag=1, deliveryAttempt=3, amqp_consumerQueue=source.events-processor, amqp_channel=Cached Rabbit Channel: AMQChannel(amqp://H), conn: Proxy@43e869ea Shared Rabbit Connection: SimpleConnection@213451a4 [delegate=amqp://], amqp_redelivered=false, id=35cdfa7f-7f75-5502-aede-6ec9569145f0, amqp_consumerTag=amq.ctag-Z87p6nKN4PLJKDbZQpXnVw, sourceData=(Body:'[B@20dc391b(byte[401])' MessageProperties [headers={}, contentLength=0, receivedDeliveryMode=NON_PERSISTENT, redelivered=false, receivedExchange=source, receivedRoutingKey=, deliveryTag=1, consumerTag=amq.ctag-Z87p6nKN4PLJKDbZQpXnVw, consumerQueue=source.events-processor]), contentType=application/json, timestamp=1599501912897}]

Figured out how to have a Function send messages to a DLQ when failed.弄清楚如何让函数在失败时向 DLQ 发送消息。 I added a Consumer also since they are related.我还添加了一个消费者,因为它们是相关的。

I believe we need to ack or reject the message, but when rejecting we want to return a Flux.empty() so nothing gets published to the downstream exchange.我相信我们需要确认或拒绝消息,但是在拒绝时我们想要返回 Flux.empty() 以便没有任何内容发布到下游交换。

Code rejects any message with fail as payload, and acks any other message.代码拒绝任何以失败作为有效负载的消息,并确认任何其他消息。

@SpringBootApplication
public class DemoApplication {

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

  @Bean
  public Function<Flux<Message<String>>, Flux<Message<String>>> transform() {
    return inbound -> inbound.flatMap(msg -> {
      try {
        Channel channel = msg.getHeaders().get(AmqpHeaders.CHANNEL, Channel.class);
        Long deliveryTag = msg.getHeaders().get(AmqpHeaders.DELIVERY_TAG, Long.class);

        if (msg.getPayload().startsWith("fail")) {
          channel.basicReject(deliveryTag, false);
        } else {
          channel.basicAck(deliveryTag, false);
          return Flux.just(msg);
        }
      } catch (IOException e) {
        e.printStackTrace();
      }

      return Flux.empty();
    });
  }

  @Bean
  public Consumer<Flux<Message<String>>> consumer() {
    return inbound -> inbound
        .map(msg -> {
          try {
            Channel channel = msg.getHeaders().get(AmqpHeaders.CHANNEL, Channel.class);
            Long deliveryTag = msg.getHeaders().get(AmqpHeaders.DELIVERY_TAG, Long.class);

            if (msg.getPayload().startsWith("fail")) {
              channel.basicReject(deliveryTag, false);
            } else {
              channel.basicAck(deliveryTag, false);
            }
          } catch (IOException e) {
            e.printStackTrace();
          }
          return msg.getPayload();
        })
        .subscribe(System.out::println);
  }
}
spring:
  profiles: consumer
  cloud:
    stream:
      function:
        definition: consumer
      bindings:
        consumer-in-0:
          destination: consumer
          group: queue
      rabbit:
        bindings:
          consumer-in-0:
            consumer:
              acknowledge-mode: MANUAL
              autoBindDlq: true
              declareDlx: true
              bindQueue: true
              deadLetterExchange: dead-letter-exchange
              prefetch: 10
---
spring:
  profiles: processor
  cloud:
    stream:
      function:
        definition: transform
      bindings:
        transform-in-0:
          destination: processorIn
          group: queue
        transform-out-0:
          destination: processorOut
      rabbit:
        bindings:
          transform-in-0:
            consumer:
              acknowledge-mode: MANUAL
              autoBindDlq: true
              declareDlx: true
              bindQueue: true
              deadLetterExchange: dead-letter-exchange
              prefetch: 10
          transform-out-0:
            producer:
              bindQueue: true
              declareExchange: true

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

相关问题 带有 Avro 消息的 Spring Cloud Stream JSON 死信队列 - Spring Cloud Stream JSON Dead Letter Queue with Avro messages Spring 云 Kafka Stream - 不同集群中的死信主题 - Spring cloud Kafka Stream - Dead Letter Topic in Different Cluster Spring Cloud Stream Binder Kafka - 不同集群中的死信主题 - Spring Cloud Stream Binder Kafka - Dead Letter Topic in Different Cluster Spring Cloud Stream:所有绑定的默认死信队列 - Spring Cloud Stream: Default dead letter queue for all bindings Spring 云 Stream RabbitMQ 在死信队列上留言 - Spring Cloud Stream RabbitMQ leave message on dead letter queue Spring Cloud Stream ServiceBus Java 用于管理死信队列 - Spring Cloud Stream ServiceBus Java for managing dead letter queues 如何在Spring Cloud Data Flow中配置Kafka绑定死信队列 - How to configure Kafka binded Dead Letter Queue in Spring Cloud Data Flow 使用Kafka活页夹在Spring Cloud Stream中重播死信队列中的消息 - Replay messages from dead letter queue in Spring Cloud Stream with Kafka binder Spring Cloud Stream:如何重新发布到死信队列并抛出异常 - Spring Cloud Stream: how to republish to dead letter queue and also throw exception 如何在 Spring Cloud Stream Kafka Binder 中为死信队列应用保留时间配置? - How to apply retention time configuration for Dead Letter Queue in Spring Cloud Stream Kafka Binder?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM