三、消息与队列

消息队列,消息队列,在了解了 AMQP 协议与 AMQP 的几个相关概念后,让我们一起来学习下创建队列的方法,以及消息在队列当中的一些动态变化。

消费者接收消息的方式

消息最终达到队列中并等待消费。消费者通过以下两种方式从特定的队列中接收消息:

  • 通过 AMQP 的 basic.consume 命令订阅。
    这样做会将信道置为接收模式,直到取消对队列的订阅为止。订阅了消息后,消费者在消费(或者拒绝)最近接收的那条消息后,就能从队列中(可用的)自动接收下一条消息。

  • 通过AMQP的basic.get命令获得单条消息。
    basic.get命令会订阅消息,获得单条消息,然后取消订阅。但是不应该将basic.get放在一个循环里来替代basic.consume。因为这样做会影响Rabbit的性能。消费者理应始终使用basic.consume来实现高吞吐量。

队列的消息分发机制

如果消息到达了无人订阅的队列时消息会在队列中等待。一旦有消费者订阅到该队列,队列上的消息就会发送给消费者;如果至少有一个消费者订阅了队列的话,队列会将收到的消息将以循环(round-robin)的方式发送给这些订阅的消费者。并且每条消息只会发送给其中的一个订阅的消费者,而不是所有订阅的消费者。

举例说明:假设有 seed_queue 队列,消费者 Farmer_one 和消费者 Farmer_two 订阅到 seed_queue 队列。当消息到达 seed_queue 队列时,消息投递方式如下:
(1)消息 Message_A 到达 seed_queue 队列。
(2)RabbitMQ 把消息 Message_A 发送给 Farmer_one。
(3)Farmer_one 确认接收到了消息 Message_A。
(4)RabbitMQ 把消息 Message_A 从 seed_queue中删除。
(5)消息 Message_B 到达 seed_queue 队列。
(6)RabbitMQ 把消息 Message_B 发送给 Farmer_two。
(7)Farmer_two 确认接收到了消息 Message_B。
(8)RabbitMQ 把消息 Message_B 从 seed_queue 中删除。

消息的确认

消费者接收到的每一条消息都必须进行确认,才能够从队列获取到下一条消息,同时 RabbitMQ 才能安全地把消息从队列中删除。这样做的目的是保证每一条消息都能够被正确消费——不重复消费、不漏消费。

举例说明:

  • 如果消费者收到一条消息,然后确认之前从 Rabbit 断开连接(或者从队列上取消订阅),RabbitMQ 会认为这条消息没有分发,然后重新分发给下一个订阅的消费者。

  • 如果应用程序有 bug 而忘记确认消息的话,Rabbit 将不会给该消费者发送更多消息了。因为在上一条消息被确认之前,Rabbit会认为这个消费者并没有准备好接收下一条消息。

提示:如果处理消息内容非常耗时,可以延迟确认该消息,直到消息处理完成。这样可以防止 Rabbit 持续不断的消息涌向你的应用而导致过载。

消息确认的方式

  • 通过 AMQP 的 basic.ack 命令显式地向 RabbitMQ 发送确认

  • 在订阅到队列的时候将 auto_ack 参数设置为 true。当设置了 auto_ack 时,一旦消费者接收消息,RabbitMQ 会自动视其确认了消息。

消息的拒绝

假设在处理消息的时候遇到了不可恢复的错误,但是由于硬件问题,只影响到当前的消费者,只要消息尚未确认,就有以下两个选择:

  • 把消费者从 RabbitMQ 服务器断开连接。
    这会导致 RabbitMQ 自动重新把消息入队并发送给另一个消费者。这样做的好处是所有的 RabbitMQ 版本都支持。缺点是,这样连接/断开连接的方式会额外增加RabbitMQ的负担。

  • 使用 AMQP 的 basic.reject 命令拒绝 RabbitMQ 发送的消息。
    如果把 reject 命令的 requeue 参数设置成true的话,RabbitMQ 会将消息重新发送给下一个订阅的消费者。如果设置成 false 的话,RabbitMQ 立即会把消息从队列中移除,而不会把它发送给新的消费者。

队列的创建

消费者和生产者都能使用 AMQP 的 queue.declare 命令来创建队列。但是如果消费者在同一条信道上订阅了另一个队列的话,就无法再声明队列了。必须首先取消订阅,将信道置为“传输”模式。如果创建时不指定队列名称的话,Rabbit 会分配一个随机名称并在 queue.declare 命令的响应中返回。

设置队列的参数:

  • exclusive ——如果设置为 true 的话,队列将变成私有的,此时只有你的应用程序才能够消费队列消息。当你想要限制一个队列只有一个消费者的时候很有帮助。

  • auto-delete ——当最后一个消费者取消订阅的时候,队列就会自动移除。如果你需要临时队列只为一个消费者服务的话,请结合使用 auto-delete 和 exclusive。当消费者断开连接时,队列就被移除了。

声明已存在的队列会怎样

只要声明参数完全匹配现存的队列的话,Rabbit 就什么都不做,并成功返回;如果参数不匹配的话,队列声明尝试会失败。

如果你只是想检测队列是否存在,则可以设置 queue.declare 的 passive 选项为 true。在该设置下,如果队列存在,那么 queue.declare 命令会成功返回;如果果队列不存在的话,queue.declare 命令不会创建队列而会返回一个错误。

队列由谁创建

生产者和消费者都可以尝试去创建队列。单纯有生产者创建会有可能导致消费者订阅到不存在的队列导致错误;单纯由消费者创建会可能导致消费者发布消息到“黑洞”。

你可能感兴趣的:(三、消息与队列)