Java应用程序使用RabbitMQ和客户端库com.rabbitmq:amqp-client
连接到它。应用程序在初始化期间声明AMQP交换,并定期向其发布消息。
如果由于某种原因导致该交换被删除,则应用程序无法向其发布消息,并且库将自动关闭AMQP通道。因此,任何后续发布(即使在重新创建交换之后)都会失败并出现此类异常:
Exception in thread "main" com.rabbitmq.client.AlreadyClosedException: channel is already closed due to channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'logs' in vhost '/', class-id=60, method-id=40)
at com.rabbitmq.client.impl.AMQChannel.ensureIsOpen(AMQChannel.java:195)
at com.rabbitmq.client.impl.AMQChannel.transmit(AMQChannel.java:296)
at com.rabbitmq.client.impl.ChannelN.basicPublish(ChannelN.java:648)
at com.rabbitmq.client.impl.ChannelN.basicPublish(ChannelN.java:631)
at com.rabbitmq.client.impl.ChannelN.basicPublish(ChannelN.java:622)
如何在发布之前保证交换确实存在?我看到以下选项。
由于exchangeDeclare
是幂等的,如果交换已经到位,则无效,我可以在任何发布之前明确声明交换:
channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false, true, null);
channel.basicPublish(EXCHANGE_NAME, "", MessageProperties.PERSISTENT_TEXT_PLAIN, message);
这段代码的问题在于它看起来很愚蠢,因为大多数时候交换已到位并且声明只是多余的。
如果在声明和发布之间删除交换,我仍然会遇到麻烦。
exchangeDeclarePassive
我可以使用exchangeDeclarePassive
在发布之前检查交换是否存在,但以下明显的方法不起作用:
private static void ensureExchangeExists(Channel channel) throws IOException {
try {
channel.exchangeDeclarePassive(EXCHANGE_NAME);
} catch (Exception e) {
channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false, true, null);
}
}
问题是如果缺少交换,exchangeDeclarePassive
会抛出异常,并且库会自动关闭该通道。所以catch块中的代码不能声明交换(因为它试图在封闭的通道上执行操作)。
所以我不能再使用单个AMQP频道,必须以某种方式管理它们。
如果在声明和发布之间删除了交换,我仍然遇到麻烦。
您不能只使用try / catch块调用channel.basicPublish,因为如果缺少交换,则不会抛出任何异常。 This answer解释了在这种情况下实际发生的事情。
但是,您可以注册能够检测频道何时关闭的ShutdownListener
,调查原因(通过cause.isInitiatedByApplication()
/ cause.getReason()
)并执行必要的操作。
问题是,在向其发布消息之前确保AMQP交换存在的最佳方法是什么?为什么?
答案 0 :(得分:0)
IME,选项#1接近更好的选择。
我发现最有效的方法是将给定发布者的代码封装成一种允许我在第一次创建对象实例时声明/重新声明队列的方式。然后我可以重新使用相同的对象实例来发布消息,而无需再次重新声明交换。
如果我创建对象的新实例,它将重新声明交换。但是重复使用相同的实例可以防止这种情况发生。
这个策略对我有用。
另一个选择是在应用启动时预先定义和预先声明您的交换,队列和绑定。那样你就不用再担心了。这里的缺点是,你是否预先确定了所有的交换,队列和绑定......这些应用程序在某些应用程序中有效但在其他应用程序中无效。
答案 1 :(得分:0)
经过一些调查后,我选择了选项#3(发布时捕获异常)和Publisher Confirms,以跟踪未成功发布的消息。