一、什么是RabbitMQ
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种语言客户端。
AMQP:即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计
rabbitMQ的工作性质类似于物流,物流公司职能就是接收、存储、转发。最终确保收件人能够及时的获取到寄件人发的物件。rabbitMQ整体工作流程来看可以分为三个工作对象:生产者(P)、队列(Quene)、消费者(C)。
生产者:发送消息的程序
消费者:接收消息的程序
队列:可以看作是一个消息缓冲器,多个生产者发送的消息进入到同一个队列中,多个消费者可以从同一个队列中接收消息并执行相应的业务逻辑。
二、RabbitMQ高性能的原因
RabbitMQ基于Erlang语言实现,而Erlang语言最初用来控制交换机或者变换协议等,非常适合构建分布式应用,它与Socket有着一样的延迟,这样使得RabbitMQ在Broker之间进行数据交互的性能非常优秀。
三、Message Acknowledgments(消息确认机制)
如果消费者C1在处理消息的过程中意外挂掉了,也就是业务逻辑没有正常执行完,那我们还希望有另外的消费者C2来继续执行这条消息,就需要保证RabbitMQ知道C1是否正常执行了该条消息,这里也就有了RabbitMQ的消息确认。C1在正常执行完消息之后会返回给RabbitMQ一个确认信息,告知RabbitMQ该条消息已经被接收、执行,RabbitMQ可以删除该消息了。有了这样的机制之后,我们就可以保证队列中的消息能够被消费者正常接收并执行了()。
从消费者到RabbitMQ的传递处理确认被称为消息传递协议中的确认。代理对发布者的确认是称为发布者确认的协议扩展 。两种功能都基于相同的思想,并受到TCP的启发。
(1)自动确认:
消费者在声明队列时,可以指定autoAck参数,当boolean autoAck=true时,一旦消费者接收到了消息,就视为自动确认了消息。如果消费者在处理消息的过程中,出了错,就没有什么办法重新处理这条消息,所以我们很多时候,需要在消息处理成功后,再确认消息,这就需要手动确认。
(2)手动确认:
1)当boolean autoAck=false时,RabbitMQ会等待消费者显式发回确认信号后才从内存(和磁盘,如果是持久化消息的话,后面会讲到消息持久化)中移去消息。否则,RabbitMQ会在队列中消息被消费后立即删除它。
2)采用消息确认机制后,只要令autoAck=false,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,因为RabbitMQ会一直持有消息直到消费者显式调用basicAck为止。
3)当autoAck=false时,对于RabbitMQ服务器端而言,队列中的消息分成了两部分:一部分是等待投递给消费者的消息;一部分是已经投递给消费者,但是还没有收到消费者ack信号的消息。如果服务器端一直没有收到消费者的确认信号,并且消费此消息的消费者已经断开连接,则服务器端会安排该消息重新进入队列,等待投递给下一个消费者。
通过消息自动确认和手动确认之间的比较可以知道,手动确认在信息安全方面更有优势,但是如果申明了手动确认,但是最后又没有发回确认信息给RabbitMQ,就会导致RabbitMQ无法及时的释放队列中的消息,而内存的消耗也会逐步增加。所以一定要注意返回确认消息
四、Message durability(消息持久化)
上面我们保证了在RabbitMQ服务正常运转情况下,队列中的消息都能够正常被消费者接收执行,但是如果RabbitMQ服务崩溃宕机,我们的消息仍会消失,这显然不是我们想要的结果。如何保证RabbitMQ服务宕机重启之后我们的消息仍然能够继续消费呢?
解决方案:消息持久化
(1)队列持久化
boolean durable = true;
channel.queueDeclare("hello", durable, false, false, null);
//"hello":申明的队列名称
我们可以通过在代码中使用以上代码申明队列持久化,以保证RabbitMQ服务重启之后,我们的队列不会消失。消息生产者和消费者代码中都需要此申明。
(2)消息持久化
import com.rabbitmq.client.MessageProperties;
channel.basicPublish("", "Hello_Java",
MessageProperties.PERSISTENT_TEXT_PLAIN,
message.getBytes());
申明了队列持久化和消息持久化之后,我们的RabbitMQ就可以保证重启之后消息队列消息不会丢失了。
前面为了简单明了说明RabbitMQ的工作流程,简化了消息转送的步骤,实际上消息由生产者发出后,先到达交换机,再由交换机路由到RabbitMQ的队列,消费者再从队列中获取到消息,下图描述了消息的正确转发步骤:
图中的 'X' 代表的就是交换机
从途中我们可以了解到,生产者不会将任何消息直接发送到RabbitMQ的队列中,而是发送给了交换机,但是交换机如何判定此条消息到底应该发送到哪个队列中?还是直接丢弃?又或者是发送到多个队列中?这就由交换机的交换机类型决定:
四、4种交换机类型
交换机可以理解成具有路由表的路由程序。每个消息都有一个称为路由键(routing key)的属性,就是一个简单的字符串。
RabbitMQ中有4种交换机类型:Direct exchange、Fanout exchange、Topic exchange、Headers exchange。
(1)Direct exchange 处理路由键
将一个队列绑定到一个交换机上,要求消息与一个特定的路由键完全匹配,例如队列绑定到交换机上要求路由键是”abc“,则只有被标记为“abc”的消息才会被转发,不会转发“abc.a”或者“abc.asfa”。注意是完全匹配!
(2)Fanout exchange 不处理路由键
此种交换机类型非常好理解,只需要将队列绑定到交换机上,发送到交换机上的消息都会被转发到与该交换机绑定的所有队列中,你可以想象一下村口放大喇叭的情景:一个人在话筒前喊话,消息通过大喇叭传播转发,此时所有能听到喇叭声(与喇叭有绑定关系的)人都能接收到该消息
(3)Topic exchange 路由键配合某模式匹配
将路由键和某种模式进行匹配,队列需要绑定到一个模式上,符号“#”匹配一个或者多个词,“*”匹配一个词。例如“abc.#”可以匹配到“abc.asfd.asga.asf”。"abc."只能匹配到“abc.asfd”。
(4)Headers exchange 根据消息的Headers属性匹配
不处理路由键,而是根据消息内容中的Headers属性进行匹配,在队列和交换机进行绑定的时候,指定一组键值对,发送消息时,消息的Headers与交换机指定的键值对进行匹配,如果完全匹配,则消息会被转发。
五、RabbitMQ架构图
下图是RabbitMQ的架构模型图,消息生产者producter只需要关注消息发送到哪个交换机,而消息消费者Consumer只需要关注连接哪个消息队列Queue。
另外一篇中间件AcitiveMQ的集群文章:https://blog.csdn.net/qq_43655835/article/details/102392005