rabbitmq学习

157 阅读6分钟

生产者

生产者发送消息到rabbitmq,其中包含路由键,交换器等信息。相应的交换器根据接收到的路由键查找相匹配的队列。如果找到,消息存入相应的队列。如果没找到,根据生产者配置的属性选择丢弃还是回退给生产者。

交换器 Exchange

生产者将消息发送到交换器,由交换器将消息路由到一个或多个队列中。如果路由不到,根据生产者配置的属性选择返回给生产者还是直接丢弃。

rabbitmq的消息存储在队列中,交换器的使用并不真正耗费服务器的性能,而队列会。

而kafka将消息存储在topic(主题)这个逻辑层面,而相应的队列逻辑只是topic实际存储文件中的位移标识。

路由键 RoutingKey

生产者将消息发给交换器时,会指定一个路由键,用来指定这个消息的路由规则,而这个路由键需要和交换器类型和绑定键(BindingKey)联合使用才能最终生效。

绑定键 BindingKey

rabbitmq通过绑定将交换器和队列关联起来,在绑定的时候一般会指定一个绑定键。

消费消息

消费者的消费模式有两种:

  1. 推模式,服务器推送
  2. 拉模式,消费者拉取

多个消费者可以订阅同一个队列,消息会被平均分摊(Round-Round,轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息。

生产者确认

默认情况下,生产者是不知道消息有没有正确到达服务器的。虽然rabbitmq支持消息持久化,但是如果消息在到达服务器之前已经丢失,谈何持久化?

针对这个问题,rabbitmq提供了两种解决方案:

  1. 事务机制
  2. 发送方确认机制 这两种机制是互斥的,只能选择其中一种。

事务机制会大大降低rabbitmq的性能,一般建议使用发送方确认机制。

生产者将信道设置成confirm模式,一旦信道进入confirm模式,所有在该信道上发布的消息都会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列后,rabbitmq会发送一个确认(Basic.Ack)给生产者(包含消息的唯一ID)。如果消息和队列是持久化的,那么确认消息会在消息写入磁盘后发出。rabbitmq回传给生产者的确认消息中的deliverTag包含了确认消息的序号,此外rabbitmq也可以设置channel.basicAck方法中的multiple参数,表示到这个序号之前的所有消息都已经得到了处理。

相比于事务,发送方确认机制最大的好处在于它是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认后,生产者可以通过回调方法来处理确认消息。如果rabbitmq因为自身内部错误导致消息丢失,会发送一条nack(Basic.Nack)命令,生产者应用程序同样可以在回调方法中处理。

事务机制和发送方确认机制确保的是消息能够正确地发送至rabbitmq的交换器。如果此交换器没有匹配的队列,那么消息也会丢失。

消息持久化

消息持久化的前提是交换器和队列持久化。设置消息持久化后,rabbitmq重启了消息依然存在。但是消息持久化不能保证数据百分百不丢失。消息写入rabbitmq后,rabbitmq不会为每条消息都进行磁盘同步(fsync),所以消息可能是写到了操作系统的缓存中而不是磁盘。如果这时候rabbitmq宕机,消息没来得及落盘,那么消息会丢失。

消费者确认与拒绝

即使设置了消息持久化,发布者确认机制,如果消费者拿到消息后挂掉了,这也算是消息丢失了。为了防止出现这种情况,要把autoAck设置为false,然后通过手动确认的方式去确认已经正确消费了消息。rabbitmq会等消费者回复了ACK才删除消息(实际上是先打上删除标记,之后再删除)。

当autoAck设置为false,消息分为两部分,一部分是等待投递的消息(Ready),另一部分是已经投递但没收到消费者确认信号的消息(Unacknowledged)。

如果消费者一直没有回复确认信号,并且消费此消息的消费者已经断开连接,rabbitMQ会安排该消息重新进入队列,等待投递给下一个消费者,当然也有可能是原来那个消费者。 rabbitmq不会为未确认的消息设置过期时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者是否已经断开连接,这么设计是因为rabbitmq允许消费者消费一条消息的时间很久很久。

如果消费者想明确拒绝当前的消息而不是确认,可以调用channel.basicReject方法告诉rabbitmq拒绝这个消息。

void basicReject(long deliveryTag, boolean requeue) throws IOException;

如果requeue参数设置为true,rabbitmq会重新将这条消息存入队列,以便可以发送给下一个订阅的消费者;如果requeue为false,rabbitmq会立刻把消息从队列中移除。

消息传输保障

“至少一次”投递需要以下几方面:

  1. 生产者需要开启事务或者确认机制。
  2. 生产者需要配合使用mandatory参数或者备份交换机来确保消息能够从交换机路由到队列,进而能够保存下来而不会被丢弃。当mandatory参数设置为true时,交换器无法根据自身的类型和路由键找到一个符合条件的队列时,rabbitmq会调用Basic.Return命令将消息返回给生产者。当mandatory设置为false时,出现上述情形,消息会被直接丢弃。
  3. 消息和队列都需要进行持久化处理。
  4. 消费者需要将autoAck设置为false,然后通过手动确认的方式去确认已经正确消费的消息,以避免在消费端引起不必要的消息丢失。