问:当一个队列被声明为没有名字时会发生什么?
A:服务器为队列选择一个唯一的名称。如果没有提供名称,RabbitMQ 服务器将生成一个唯一的 RabbitMQ 集群名称,使用该名称创建一个队列,然后将该名称传输回调用queue.declare
. RabbitMQ 在内部以线程安全的方式执行此操作(例如,许多queue.declare
使用空白名称调用的客户端将永远不会获得相同的名称)。这是有关此行为的文档。
问:队列名称是否需要全局唯一?
答:不,但它们可能需要在您的用例中。任意数量的发布者和订阅者可以共享一个队列。队列声明是幂等的,因此如果 2 个客户端同时或在不同时间声明一个具有相同名称和设置的队列,则服务器状态将与只有一个客户端声明它时相同。但是,具有空白名称的队列永远不会发生冲突。考虑用一个空白名称声明一个队列,就好像它是两个操作一样:一个 RPC 要求 RabbitMQ “给我一个全局唯一的名称,您将保留它仅供我使用”,然后幂等地声明一个具有该名称的队列。
问:queue.bind
在多线程环境中,空白名称会绑定到最后创建的队列吗?
A:是的,但你不应该那样做;它一无所获,令人困惑,并且具有未指定/未指定的行为。这种技术在很大程度上是没有意义的并且容易在客户端代码中出现错误(如果在声明和添加之间添加了行怎么办?那么很难确定正在绑定哪个队列)。
queueDeclare
相反,使用;的返回值 该返回值将包含已声明的队列的名称。如果您声明了一个没有名称的队列,则 的返回值queueDeclare
将包含 RabbitMQ 提供的新的全局唯一名称。您可以将其显式提供给使用该队列的后续调用(例如绑定它)。
另一个不这样做的原因是,有关空白队列名称行为的文档非常模棱两可:
客户端必须要么指定一个队列名称,要么之前已经在同一通道上声明了一个队列
这意味着什么?如果声明了多个队列,将绑定哪个队列?如果之前声明的队列在同一个频道上被删除了怎么办?这似乎是尽可能明确而不依赖这种行为的一个很好的理由。
问:队列是否可以删除连接到它们的“下方”通道?
A:是的,在特定情况下。对您的问题术语的细微说明:通道不会将自己“绑定”到队列:但是,通道可以使用队列。将通道想象成网络端口,将队列想象成远程对等点:您不会将端口绑定到远程对等点,但您可以通过同一个端口与多个对等点通信。消费者相当于连接的套接字;不是渠道。反正:
通道在这里无关紧要,但消费者和连接很重要(每个通道可以有多个消费者,甚至是同一个队列;每个连接可以有多个通道)。以下是可以在订阅它的频道“下方”删除队列的情况(我可能错过了一些,但这些都是非灾难性的——例如我所知道的“服务器爆炸”的情况):
- 声明了一个队列并
exclusive
设置为 true,并且声明该队列的连接关闭。用于声明队列的通道可以关闭,但只要连接保持打开状态,队列就会保持存在。连接到独占队列的客户端将看到它消失。但是,如果独占队列被“锁定”到其声明者,客户端可能无法首先访问用于消费的独占队列——文档不清楚“使用”在独占锁定方面的含义。
- 通过
queue.delete
调用手动删除的队列。在这种情况下,所有连接到队列的消费者在下次尝试使用它时可能会遇到错误。
请注意,在许多客户端情况下,消费者通常足够“被动”,以至于他们不会意识到队列已经消失;他们只会永远听什么实际上是一个封闭的套接字。发布到队列,或尝试用passive
(existence poll) 重新声明它可以保证显示不存在;单独消费是不行的:有时你会看到一个“这个队列被删除了!” 错误,有时需要几分钟或几小时才能到达,有时如果您所做的只是消耗,您将永远不会看到这样的错误。
问:auto_delete
当另一个消费者退出时,队列会在一个消费者“下方”被删除吗?
A:不会。队列会在 最后一个消费者离开队列auto_delete
后的某个时间被删除。因此,如果您在 auto_delete 队列上启动两个消费者,您可以退出其中一个而不会打扰另一个。这是有关该行为的文档。
此外,过期的队列(通过每个队列的 TTL)遵循相同的行为:队列只会在最后一个消费者离开后的某个时间消失。