RabbitMQ原理

1 RabbitMQ原理

RabbitMQ是一个由Erlang语言开发的AMQP的开源实现
AMQPAdvanced Message Queue Protocol,高级消息队列协议,它是应用层协议的一个开放标准

1.1 RabbitMQ原理图

在这里插入图片描述
在这里插入图片描述

1.2 原理讲解

1.2.1 Message消息

消息是不具名的, 它由消息头消息体组成。消息体是不透明的,而消息头则由一系列可选属性组成,这些属性包括:routing-key(路由键)、priority(相对于其他消息的优先权)、 delivery-mode(指出消息可能持久性存储)等。

1.2.2 Publisher消息生产者

消息生产者,是一个向交换器发布消息的客户端应用程序。

1.2.3 Consumer消息消费者

表示一个从消息队列中取得消息的客户端应用程序。

1.2.4 Exchange交换器

用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
三种常用的交换器类型:

  • direct(发布与订阅 完全匹配)
  • fanout(广播)
  • topic(主题, 规则匹配)

1.2.5 Binding绑定

用于消息队列交换器之间的关联。 一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则, 所以可以将交换器理解成一个由绑定构成的路由表

1.2.6 Queue消息队列

用来保存消息直到发送给消费者。 它是消息的容器, 也是消息的终点。 一个消息可投入一个或多个队列。 消息一直在队列里面, 等待消费者链接到这个队列将其取走。

1.2.7 Routing-key路由键

RabbitMQ决定消息该投递到哪个队列的规则。队列通过路由键绑定到交换器。
消息发送到MQ服务器时,消息将拥有一个路由键, 即便是空的,RabbitMQ也会将其和绑定使用的路由键进行匹配。
如果相匹配, 消息将会投递到该队列。
如果不匹配, 消息将会进入黑洞。

1.2.8 Connection链接

rabbit服务器和服务建立的TCP链接。

1.2.9 Channel信道

  • Channel中文叫做信道, 是 TCP 里面的虚拟链接。 例如: 电缆相当于TCP, 信道是一个独立光纤束, 一条 TCP 连接上创建多条信道是没有问题的。
  • TCP 一旦打开, 就会创建AMQP信道。
  • 无论是发布消息接收消息订阅队列, 这些动作都是通过信道完成的。

1.2.10 Virtual Host虚拟主机

表示一批交换器消息队列相关对象
虚拟主机是共享相同的身份认证加密环境的独立服务器域。 每个vhost本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列、 交换器、 绑定和权限机制。
vhostAMQP概念的基础, 必须在链接时指定,RabbitMQ默认的vhost/

1.2.11 Borker服务器实体

表示消息队列服务器实体

1.2.12 交换器和队列的关系

交换器是通过路由键队列绑定在一起的, 如果消息拥有的路由键跟队列和交换器的
路由键匹配, 那么消息就会被路由到该绑定的队列中。
也就是说, 消息到队列的过程中, 消息首先会经过交换器, 接下来交换器在通过路由
键匹配分发消息到具体的队列中。
路由键可以理解为匹配的规则。

1.2.13 RabbitMQ为什么需要信道,为什么不是TCP直接通信

  1. TCP的创建和销毁开销特别大。 创建需要3次握手, 销毁需要4次挥手。
  2. 如果不用信道, 那应用程序就会以TCP链接Rabbit, 高峰时每秒成千上万条链接会造成资源巨大的浪费,而且操作系统每秒处理 TCP 链接数也是有限制的, 必定造成性能瓶颈。
  3. 信道的原理是一条线程一条通道,多条线程多条通道同用一条TCP链接。一条TCP链接可以容纳无限的信道, 即使每秒成千上万的请求也不会成为性能的瓶颈。

1.3 RabbitMQ中的消息确认ACK机制

1.3.1 消息确认ACK理解

如果在处理消息的过程中,消费者的服务器在处理消息时出现异常,那可能这条正在处理的消息就没有完成消息消费,数据就会丢失,为了确保数据不丢失,RabbitMQ支持消息确认-ACK

1.3.2 ACK的消息确认机制

ACK机制是消费者从RabbitMQ收到消息并处理完成后,反馈给RabbitMQRabbitMQ收到反馈后才将此消息从队列中删除

  1. 如果一个消费者在处理消息出现了网络不稳定、服务器异常等现象,那么就不会有ACK反馈,RabbitMQ会认为这个消息没有正常消费,会将消息重新放入队列中`
  2. 如果在集群情况下,RabbitMQ会立即将这个消息推送给这个在线的其他消费者。这种机制保证了在消费者服务端故障的时候,不丢失任何消息和任务
  3. 消息永远不会从RabbitMQ中删除:只有当消费者正确发送ACK反馈,RabbitMQ确认收到后,消息才会从RabbitMQ服务器的数据中删除
  4. 消息的ACK确认机制默认是打开的

1.3.3 ACK机制的注意

如果忘记了ACK,那么后果很严重。当consumer退出时,Message会一直重新分发。
然后RabbitMQ占用越来越多的内存,由于RabbitMQ会长时间运行,因此内存泄漏是致命的

1.3.4 接受方出现异常

因为接受方consumer出现异常,消息会放到队列里面,而导致这个消息不会删除而一直都是那个消息重复发送,为了预防网络波动的异常,而不得不使用原来的消息,就应该开启重试机制,而应该使用如下配置。

#开启重试
spring.rabbitmq.listener.simple.retry.enabled=true
#重试次数, 默认为 3 次
spring.rabbitmq.listener.simple.retry.max-attempts=3

1.4 RabbitMQ特性

1.4.1 顺序性

RabbitMQ 把消息放入到对应的队列后,我们紧接着面临的问题就是,我们应该在系统内部启动多少线程去从消息队列中获取消息。

如果只是单线程去获取消息,那自然没有什么好说的。但是多线程情况,可能就会有问题了……
RabbitMQ 有这么个特性,它在官方文档就声明了自己是不保证多线程消费同一个队列的消息,一定保证顺序的。而不保证的原因,是因为多线程时,当一个线程消费消息报错的时候,RabbitMQ 会把消费失败的消息再入队,此时就可能出现乱序的情况。

1.4.2 消息延迟性

RabbitMQ 的消息自带手表,消息中有个 TTL 字段,可以设置消息在 RabbitMQ 中的存放的时间,超时了会被移送到一个叫死信队列的地方。
所以,延迟队列 RabbitMQ 最简单的实现方式就是设置 TTL,然后一个消费者去监听死信队列。当消息超时了,监听死信队列的消费者就收到消息了。

不过,这样做有个大问题:假设,我们先往队列放入一条过期时间是 10 秒的 A 消息,再放入一条过期时间是 5 秒的 B 消息。那么问题来了,B 消息会先于 A 消息进入死信队列吗?

答案是否定的。B 消息会优先遵守队列的先进先出规则,在 A 消息过期后,和其一起进入死信队列被消费者消费。

RabbitMQ3.5.8 版本以后,官方推荐的 rabbitmq delayed message exchange 插件可以解决这个问题。用了这个插件,我们在发送消息的时候,把消息发往一个特殊的 Exchange。同时,在消息头里指定要延迟的时间。收到消息的 Exchange 并不会立即把消息放到队列里,而是在消息延迟时间到达后,才会把消息放入。
在这里插入图片描述

1.4.3 消息保持

在微服务里,事件溯源模式是经常用到的。如果想用消息队列实现,一般是把事件当成消息,依次发送到消息队列中。

事件溯源有个最经典的场景,就是事件重放。简单来讲就是把系统中某段时间发生的事件依次取出来再处理。而且,根据业务场景不同,这些事件重放很可能不是一次,更可能是重复 N 次。

假设,我们现在需要一批在线事件重放,去排查一些问题。
RabbitMQ 此时就真的不行了,因为消息被人取出来就被删除了,想再次被重复消费,可能做不到。
但是可以使用 Kafka,消息会被持久化一个专门的日志文件里,不会因为被消费了就被删除

1.4.4 消息错误处理

RabbitMQ 由于会在消息出问题或者消费错误的时候,可以重新入队或者移动消息到死信队列,继续消费后面的,会省心很多

1.4.5 消息的吞吐量

Kafka 是每秒几十万条消息吞吐,RabbitMQ 的吞吐量是每秒几万条消息

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值