我正在尝试在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中的头文件交换来确认交付?
注意:我已经对队列路由设置进行了“复杂”交换,我宁愿避免在游戏中添加死信路由,也只是发送失败。
答案 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()。