Broker 语义
此处我们描述 Broker 的语义。应与 AMQP 规范一起阅读。
tx 的语义
AMQP 0-9-1 中定义的 AMQP tx 类的语义,以及其在不同版本的 RabbitMQ 服务器中的实现,常常被误解。以下是行为的总结:
| 功能 | AMQP | RabbitMQ | RabbitMQ | RabbitMQ |
|---|---|---|---|---|
| 事务性 basic.publish | 是 | 是 | 是 | 是 |
| 事务性 basic.ack | 是 | 是 | 是 | 是 |
| 事务性 basic.reject | 否 | 否 | 否 | 是 |
| 事务性交换器/队列/绑定创建/删除 | 否 | 否 | 否 | 否 |
| 事务性消息的消费/获取 | 否 | 否 | 否 | 否 |
| 单个队列的原子性 | 是 | 否 | 否 | 否 |
| 跨多个队列的原子性 | 否 | 否 | 否 | 否 |
| 错误检测(例如,无效的交换器) | 未定义 | 即时 | 即时 | 即时 |
| 发送 'no_route' basic.return | 未定义 | 即时 | 提交时 | 提交时 |
| 效果可见性 / 责任转移 / 持久性 | 未定义 | 提交时 | 提交时 | 提交时 |
总的来说,AMQP tx 类的行为,尤其是在 RabbitMQ 上的实现,更像是提供一个“批量处理”功能,而不是数据库世界中已知的 ACID 功能。
AMQP 事务仅适用于发布和确认。我们额外地使拒绝操作事务化。其他操作,如资源创建/删除,不是事务性的。因此,当任何涉及的交换器、队列或绑定被修改时,事务的行为是未定义的。
在消费端,确认是事务性的,消息的消费本身不是。因此,回滚时不会重新排队已消费的消息;客户端仍然可以在后续的事务中确认/拒绝这些消息。
AMQP 仅在事务涉及单个队列时保证原子性,即 tx 中的所有发布都路由到单个队列,并且所有确认都与从同一队列消费的消息相关。当涉及多个队列时,如果 Broker 在 tx.commit 期间发生故障,事务的效果可能仅在某些队列中可见。此外,即使涉及单个队列的事务,RabbitMQ 也不提供原子性保证,例如,tx.commit 期间的故障可能导致 Broker 重启后,事务发布的部分子集出现在队列中。
AMQP 没有规定何时检测事务性 basic.publish 和 basic.ack 命令中的错误(例如,权限不足、引用未知交换器)。RabbitMQ 会立即执行必要的检查(而不是在提交时),但请注意,basic.publish 和 basic.ack 都是异步命令,因此任何错误都会异步报告给客户端。
对于 basic.returns 的情况也类似,但请注意 RabbitMQ 早期和近期版本之间行为的轻微变化。您总是在 tx.commit-ok 之前收到任何 basic.return。
AMQP 没有规定事务效果在 tx.commit 后何时可见,例如,已发布的が消息何时出现在队列中可供其他客户端消费,持久化消息何时写入磁盘等。在 RabbitMQ 中,tx.commit-ok 表明所有事务效果都已可见,并且 Broker 已接受事务中所有已发布消息的责任。
对于确认,收到 tx.commit-ok 表明服务器已收到确认,而不是表明它们已被处理、持久化等。因此,后续的服务器端故障可能“复活”已确认的消息,并且消费客户端可能会再次收到它们。
消息排序保证
AMQP 0-9-1 核心规范的第 4.7 节解释了保证排序的条件:在一个通道中发布,通过一个交换器、一个队列和一个出站通道的消息,将以它们发送的相同顺序接收。RabbitMQ 自 2.7.0 版本以来提供了更强的保证。
消息可以通过具有 requeue 参数的 AMQP 方法(basic.recover, basic.reject 和 basic.nack)返回队列,或者由于通道在持有未确认消息时关闭。对于 RabbitMQ 2.7.0 之前的版本,这些情况中的任何一种都会导致消息被重新排队到队列的末尾。从 RabbitMQ 2.7.0 版本开始,即使存在重新排队或通道关闭的情况,消息也始终按发布顺序保留在队列中。
在 2.7.0 及更高版本中,如果队列有多个订阅者,单个订阅者仍然可能观察到消息乱序。这是由于其他订阅者重新排队消息的操作所致。从队列的角度来看,消息始终按发布顺序保留。
独占队列和持久性
独占队列是指只要声明它的连接关闭,该队列就会被删除。尽管 AMQP 0-9-1 允许您声明一个持久化的独占队列,但其持久性毫无意义,因为一旦 Broker 停止,该队列就会消失。因此,RabbitMQ 将忽略声明独占队列时的持久化标志,并创建一个独占的临时队列。
获取帮助和提供反馈
如果您对本指南的内容或与 RabbitMQ 相关的任何其他主题有疑问,请随时通过 GitHub Discussions 或我们的社区 Discord 服务器 提问。