Rabbitmq标题交换并确认交付

时间:2017-05-09 09:55:46

标签: java python rabbitmq rabbitmq-exchange

我正在尝试在RabbitMQ上使用标头交换,使用混合的java和python组件,我需要确认交付。

我似乎从python(pika)和java客户端获得了不同的行为。

在python中:

channel.exchange_declare(exchange='headers_test',
¦   ¦   ¦   ¦   ¦   ¦   ¦type='headers',
¦   ¦   ¦   ¦   ¦   ¦   ¦durable=True)
channel.confirm_delivery()
result = channel.basic_publish(exchange='headers_test',
¦   ¦   ¦   ¦   ¦   ¦ routing_key='',
¦   ¦   ¦   ¦   ¦   ¦ mandatory=True,
¦   ¦   ¦   ¦   ¦   ¦ body=message,
¦   ¦   ¦   ¦   ¦   ¦ properties=pika.BasicProperties(
¦   ¦   ¦   ¦   ¦   ¦   ¦ delivery_mode=2,
¦   ¦   ¦   ¦   ¦   ¦   ¦ headers=message_headers))

如果标题与任何绑定的使用者不匹配且无法路由邮件,结果为false

但是在java / scala中:

channel.exchangeDeclare("headers_test", "headers", true, false, null)
channel.confirmSelect

val props = MessageProperties.PERSISTENT_BASIC.builder
¦   ¦   ¦   ¦  .headers(messageHeaders).build
channel.basicPublish("headers_test", 
¦   ¦   ¦   ¦   ¦   ¦"", //routingKey
¦   ¦   ¦   ¦   ¦   ¦true, //mandatory
¦   ¦   ¦   ¦   ¦   ¦props, 
¦   ¦   ¦   ¦   ¦   ¦"data".getBytes)
channel.waitForConfirmsOrDie()

此处,当messageHeaders找不到匹配项时,消息似乎只是被删除而没有错误

我错过了什么或两个客户的行为真的不同吗?如何使用java中的头文件交换来确认交付?

注意:我已经对队列路由设置进行了“复杂”交换,我宁愿避免在游戏中添加死信路由,也只是发送失败。

1 个答案:

答案 0 :(得分:2)

即使没有与您的标头匹配的队列,也会将邮件视为已确认。来自文档(https://www.rabbitmq.com/confirms.html):

  

对于不可路由的消息,经纪人会在一旦发出确认   exchange验证不会路由到任何队列的消息(返回空   队列列表)。如果消息也是强制发布的,那么   basic.return在basic.ack之前发送给客户端。同样如此   否定确认(basic.nack)。

相反,您应该检查basic.return消息以检测消息是否已被路由。

我已经通过wireshark检查了,事实上我可以看到,如果邮件没有路由到那里,那就是AMQP basic.return消息。

我认为你应该从

开始
channel.addReturnListener(new ReturnListener() {
  @Override
  public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
    System.out.println("App.handleReturn");
    System.out.println("replyCode = [" + replyCode + "], replyText = [" + replyText + "], exchange = [" + exchange + "], routingKey = [" + routingKey + "], properties = [" + properties + "], body = [" + body + "]");
  }
});

事实上,如果邮件没有被路由,我会得到这个:

  

replyCode = [312],replyText = [NO_ROUTE],exchange = [headers_logs],   routingKey = [],亲......

此外,如果你想模仿Pika在Java中的同步行为,你似乎可以通过在发布消息和注册确认监听器而不是依赖.waitForConfirmsOrDie()之前获取当前发布序列号来实现。

所以完整的代码示例将是:

channel.addReturnListener(new ReturnListener() {
      @Override
      public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.out.println("App.handleReturn");
        System.out.println("replyCode = [" + replyCode + "], replyText = [" + replyText + "], exchange = [" + exchange + "], routingKey = [" + routingKey + "], properties = [" + properties + "], body = [" + body + "]");
      }
    });

    channel.addConfirmListener(new ConfirmListener() {
      @Override
      public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("App.handleAck");
        System.out.println("deliveryTag = [" + deliveryTag + "], multiple = [" + multiple + "]");
      }

      @Override
      public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("App.handleNack");
        System.out.println("deliveryTag = [" + deliveryTag + "], multiple = [" + multiple + "]");
      }
});

long nextPublishSeqNo = channel.getNextPublishSeqNo();
System.out.println("nextPublishSeqNo = " + nextPublishSeqNo);

channel.basicPublish("headers_logs",
    "",
     true,
     props,
    "data".getBytes());

在返回/确认回调中,您需要查找在发布消息之前获得的频道的发布序列号。

如果您查看线路上发生的情况,如果消息未被路由到任何队列,RabbitMq会发回一条basic.return消息,该消息还包含确认(传递标记)。如果消息已被路由,RabbitMq会发回一条bacic.ack消息,其中还包含一条确认消息。

似乎RabbitMq Java客户端总是在basicConfirm()之前调用basicReturn()回调,因此确定消息是否已被路由的逻辑可以是:

注册返回并确认频道上的听众; 记住频道的下一个发布序列号; 等待返回或确认回调。如果它是返回回调 - 尚未路由消息,您应忽略对同一传递标记的进一步确认。如果在收到handleReturn()之前收到handleAck()回调,则表示消息已路由到队列。

虽然我不确定在哪种情况下可以调用.handleNack()。