RabbitMQ消息机制的原理
一.消息通信模式(消费者、生产者和代理模式)
RabbitMQ中,其通信模式不再是传统的服务器\客户端模型(B\S模型),而是一种新型的消费者、生产者和代理的模型。
通信过程:生产者(producer)负责创建消息,并发布到代理服务器(RabbitMQ)即可。消费者连接代理服务器,订阅到自身的队列(queue)上,即可消费自己关注的消息。其中,消息包含两部分内容:有效载荷(payload)和标签(label)。有效载荷就是真实需要传输的数据,而标签用来标记消息,让RabbitMQ将消息发送到感兴趣的消费者。其流程大致如下:
无论是作为生产者,发布消息之前,还是作为消费者,消费消息之前,应用程序都需要和Rabbit代理服务器创建一条TCP连接。而后,应用程序创建AMPQ信道将AMQP命令发送出去,实现发布消息、订阅队列或接收消息。信道是建立在“真实的”TCP连接内的虚拟连接。同时,每个信道将会被指派一个唯一ID。
为什么需要信道,而不是通过TCP连接呢?主要原因在于对os(操作系统)而言建立和销毁TCP的开销非常昂贵。而当我们为所有线程只使用一个TCP连接以满足需求时,但又为了能确保每个线程的私密性,因此创建私密通信路径——信道。信道类似于使用的光缆,一根光缆代表TCP连接,而内部的不同光纤束代表不同的信道,保证通信的高效进行。
二.AMQP的组成(交换器、队列和绑定)
AMQP消息路由包括三个部分:交换器、队列和绑定。
队列
对于队列来说,消费者接收信息一般有两种方式:1)订阅方式,采用basic.consume。只要有消息来临,消费者就会自动接收。2)单条接收,采用basic.get。消费者每次从队列中只获取一条信息。另外,多个消费者可以订阅同一个队列。
拒绝接收消息的方式:
1.当消费者和RabbitMQ服务器断开连接。RabbitQM自动会把消息重新入队,发给另外一个消费者。
2.如果想要拒绝接收信息,可以采用basic.reject的命令。当reject的requeue参数设定为true,RabbitMQ会将消息重新发送给下一个消费者,而设定为false,RabbitMMQ会将消息从队列移除,不发送给下一个消费者。
对负载均衡来说,队列是绝佳的方案。
交换器和绑定
作用:当想要将消息发送给队列时,首先会发送给交换器。然后根据确定的规则(路由键),决定消息投递到那个队列/而队列通过路由键绑定到交换器。
好处:1)可以完成不同的使用场景;2)对于发送消息给服务器的发布者来说,无需关心服务的另外一端的逻辑(即队列和消费者)。
交换器包括四种类型:direct、fanout、topic和headers。
三.虚拟主机
为了将多个Rabbit的客户分开,RabbitMQ提供了vhost的概念。vhost作用:通过在各个实例间提供逻辑上分离,使得不同应用程序安全保密的运行数据。不仅将统一Rabbit的客户区分开,而且避免队列和交换器的命名冲突。因此,只需要运行一个Rabbit,然后按需启动和关闭vhost。
查看Rabbit运行哪些vhost:rabbitmqctl list_vhosts
创建vhost:rabbitmqctl add_vhost [vhost_name]
删除vhost:rabbitmqctl delete_vhost [vhost_name]
四.持久化
为了防止重启和掉电而导致队列和交换器消失,设定每个队列和交换器的durable属性为True。(注:这并不能保证数据不丢失。)
能从AMQP服务器崩溃中恢复数据,称之为持久化消息。为了达到这种目的,我们需要保证消息:1)把它的投递模式选项(delivery_mode)设置为2;2)发送到持久化交换器;3)到达持久化队列。
由于需要满足上述三个条件,数据才能实现持久化。那么如果当进行到第二步,出现宕机,消息也同样无法持久化。因此引入了另外两种持久化方法:a)AMQP事务;b)发送方确认模式。一般情况,建议使用第二种。
AMQP事务是将信道设置成事务模式,然后通过信道发送所有想确认的消息,一旦完成,提交事务。如果第一条命令执行失败,后面的命令也将会被忽略。但是由于事务的粒度较粗,大大降低了RabbitMQ的效率,因此引入了发送方确认模式。
发送方确认模式使得生产者可以异步的发送消息,同时异步的接收确认。如果消息丢失,会收到一个nack消息,如果成功,则会收到一个ack消息。同时,没有了消息回滚的概念(同事务相比),使得对服务器的性能影响可以忽略不计。