资讯动态

【消息队列】 原理初探之RabbitMQ

发布时间:2026/10/9 12:36:33 来源:尧图企业网站定制
RabbitMQ是使用Erlang语言来编写的并且RabbitMQ是基于AMQP协议的。Erlang语言在数据交互方面性能优秀有着和原生Socket一样的延迟这也是RabbitMQ高性能的原因所在。可谓“人如其名”RabbitMQ像兔子一样迅速。3.1 基本概念提到RabbitMQ就不得不提AMQP协议。AMQP协议是具有现代特征的二进制协议。是一个提供统一消息服务的应用层标准高级消息队列协议是应用层协议的一个开放标准为面向消息的中间件设计。先了解一下AMQP协议中间的几个重要概念Server接收客户端的连接实现AMQP实体服务。Connection连接应用程序与Server的网络连接TCP连接。Channel信道消息读写等操作在信道中进行。客户端可以建立多个信道每个信道代表一个会话任务。Message消息应用程序和服务器之间传送的数据消息可以非常简单也可以很复杂。由Properties和Body组成。Properties为外包装可以对消息进行修饰比如消息的优先级、延迟等高级特性Body就是消息体内容。Virtual Host虚拟主机用于逻辑隔离。一个虚拟主机里面可以有若干个Exchange和Queue同一个虚拟主机里面不能有相同名称的Exchange或Queue。Exchange交换器接收消息按照路由规则将消息路由到一个或者多个队列。如果路由不到或者返回给生产者或者直接丢弃。RabbitMQ常用的交换器常用类型有direct、topic、fanout、headers四种后面详细介绍。Binding绑定交换器和消息队列之间的虚拟连接绑定中可以包含一个或者多个RoutingKey。RoutingKey路由键生产者将消息发送给交换器的时候会发送一个RoutingKey用来指定路由规则这样交换器就知道把消息发送到哪个队列。路由键通常为一个“.”分割的字符串例如“com.rabbitmq”。Queue消息队列用来保存消息供消费者消费。3.2 系统架构3.2.1 整体架构AMQP协议模型由三部分组成生产者、消费者和服务端。生产者是投递消息的一方首先连接到Server建立一个连接开启一个信道然后生产者声明交换器和队列设置相关属性并通过路由键将交换器和队列进行绑定。同理消费者也需要进行建立连接开启信道等操作便于接收消息。接着生产者就可以发送消息发送到服务端中的虚拟主机虚拟主机中的交换器根据路由键选择路由规则然后发送到不同的消息队列中这样订阅了消息队列的消费者就可以获取到消息进行消费。总结一下整体过程生产者投递消息 - 和Server建立连接开启信道 - 声明交换器和队列并通过路由键将交换机和队列绑定 - 投递消息到虚拟主机 - 消息发送到消息队列 - 消费者建立连接 - 消费消息 - 关系信道和连接。3.3 常用交换器RabbitMQ常用的交换器类型有direct、topic、fanout、headers四种Direct Exchange见文知意直连交换机意思是此交换机需要绑定一个队列要求该消息与一个特定的路由键完全匹配。简单点说就是一对一的点对点的发送。Fanout Exchange这种类型的交换机需要将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播每台子网内的主机都获得了一份复制的消息。简单点说就是发布订阅。Topic Exchange直接翻译的话叫做主题交换机如果从用法上面翻译可能叫通配符交换机会更加贴切。这种交换机是使用通配符去匹配路由到对应的队列。通配符有两种* 、 #。需要注意的是通配符前面必须要加上.符号。* 符号有且只匹配一个词。比如 a.*可以匹配到a.b、a.c但是匹配不了a.b.c。# 符号匹配一个或多个词。比如rabbit.#既可以匹配到rabbit.a.b、rabbit.a也可以匹配到rabbit.a.b.c。Headers Exchange这种交换机用的相对没这么多。它跟上面三种有点区别它的路由不是用routingKey进行路由匹配而是在匹配请求头中所带的键值进行路由。创建队列需要设置绑定的头部信息有两种模式全部匹配和部分匹配。如上图所示交换机会根据生产者发送过来的头部信息携带的键值去匹配队列绑定的键值路由到对应的队列。3.4 消费原理我们先看几个基本概念broker每个节点运行的服务程序功能为维护该节点的队列的增删以及转发队列操作请求。master queue每个队列都分为一个主队列和若干个镜像队列。mirror queue镜像队列作为master queue的备份。在master queue所在节点挂掉之后系统把mirror queue提升为master queue负责处理客户端队列操作请求。注意mirror queue只做镜像设计目的不是为了承担客户端读写压力。集群中有两个节点每个节点上有一个broker每个broker负责本机上队列的维护并且borker之间可以互相通信。集群中有两个队列A和B每个队列都分为master queue和mirror queue备份。那么队列上的生产消费怎么实现的呢对于消费队列如下图有两个consumer消费队列A这两个consumer连在了集群的不同机器上。RabbitMQ集群中的任何一个节点都拥有集群上所有队列的元信息所以连接到集群中的任何一个节点都可以主要区别在于有的consumer连在master queue所在节点有的连在非master queue节点上。因为mirror queue要和master queue保持一致故需要同步机制正因为一致性的限制导致所有的读写操作都必须都操作在master queue上想想为啥读也要从master queue中读和数据库读写分离是不一样的然后由master节点同步操作到mirror queue所在的节点。即使consumer连接到了非master queue节点该consumer的操作也会被路由到master queue所在的节点上这样才能进行消费。对于生成队列原理和消费一样如果连接到非 master queue 节点则路由过去。所以到这里小伙伴们就可以看到 RabbitMQ的不足由于master queue单节点导致性能瓶颈吞吐量受限。虽然为了提高性能内部使用了Erlang这个语言实现但是终究摆脱不了架构设计上的致命缺陷。3.5 高级特性3.5.1 过期时间Time To Live也就是生存时间是一条消息在队列中的最大存活时间单位是毫秒下面看看RabbitMQ过期时间特性RabbitMQ可以对消息和队列设置TTL。RabbitMQ支持设置消息的过期时间在消息发送的时候可以进行指定每条消息的过期时间可以不同。RabbitMQ支持设置队列的过期时间从消息入队列开始计算直到超过了队列的超时时间配置那么消息会变成死信自动清除。如果两种方式一起使用则过期时间以两者中较小的那个数值为准。当然也可以不设置TTL不设置表示消息不会过期如果设置为0则表示除非此时可以直接将消息投递到消费者否则该消息将被立即丢弃。3.5.2 消息确认为了保证消息从队列可靠地到达消费者RabbitMQ提供了消息确认机制。消费者订阅队列的时候可以指定autoAck参数当autoAck为true的时候RabbitMQ采用自动确认模式RabbitMQ自动把发送出去的消息设置为确认然后从内存或者硬盘中删除而不管消费者是否真正消费到了这些消息。当autoAck为false的时候RabbitMQ会等待消费者回复的确认信号收到确认信号之后才从内存或者磁盘中删除消息。消息确认机制是RabbitMQ消息可靠性投递的基础只要设置autoAck参数为false消费者就有足够的时间处理消息不用担心处理消息的过程中消费者进程挂掉后消息丢失的问题。3.5.3 持久化消息的可靠性是RabbitMQ的一大特色那么RabbitMQ是如何保证消息可靠性的呢答案就是消息持久化。持久化可以防止在异常情况下丢失数据。RabbitMQ的持久化分为三个部分交换器持久化、队列持久化和消息的持久化。交换器持久化可以通过在声明队列时将durable参数设置为true。如果交换器不设置持久化那么在RabbitMQ服务重启之后相关的交换器元数据会丢失不过消息不会丢失只是不能将消息发送到这个交换器了。队列的持久化能保证其本身的元数据不会因异常情况而丢失但是不能保证内部所存储的消息不会丢失。要确保消息不会丢失需要将其设置为持久化。队列的持久化可以通过在声明队列时将durable参数设置为true。设置了队列和消息的持久化当RabbitMQ服务重启之后消息依然存在。如果只设置队列持久化或者消息持久化重启之后消息都会消失。当然也可以将所有的消息都设置为持久化但是这样做会影响RabbitMQ的性能因为磁盘的写入速度比内存的写入要慢得多。对于可靠性不是那么高的消息可以不采用持久化处理以提高整体的吞吐量。鱼和熊掌不可兼得关键在于选择和取舍。在实际中需要根据实际情况在可靠性和吞吐量之间做一个权衡。3.5.4 死信队列当消息在一个队列中变成死信之后他能被重新发送到另一个交换器中这个交换器成为死信交换器与该交换器绑定的队列称为死信队列。消息变成死信有下面几种情况消息被拒绝。消息过期队列达到最大长度DLX也是一个正常的交换器和一般的交换器没有区别他能在任何的队列上面被指定实际上就是设置某个队列的属性。当这个队列中有死信的时候RabbitMQ会自动将这个消息重新发送到设置的交换器上进而被路由到另一个队列我们可以监听这个队列中消息做相应的处理。死信队列有什么用当发生异常的时候消息不能够被消费者正常消费被加入到了死信队列中。后续的程序可以根据死信队列中的内容分析当时发生的异常进而改善和优化系统。3.5.5 延迟队列一般的队列消息一旦进入队列就会被消费者立即消费。延迟队列就是进入该队列的消息会被消费者延迟消费延迟队列中存储的对象是的延迟消息“延迟消息”是指当消息被发送以后等待特定的时间后消费者才能拿到这个消息进行消费。延迟队列用于需要延迟工作的场景。最常见的使用场景淘宝或者天猫我们都使用过用户在下单之后通常有30分钟的时间进行支付如果这30分钟之内没有支付成功那么订单就会自动取消。除了延迟消费延迟队列的典型应用场景还有延迟重试。比如消费者从队列里面消费消息失败了可以延迟一段时间以后进行重试。3.5 特性分析这里才是内容的重点不仅需要知道RabbitMQ的特性还需要知道支持这些特性的原因消息路由支持RabbitMQ可以通过不同的交换器支持不同种类的消息路由消息有序不支持当消费消息时如果消费失败消息会被放回队列然后重新消费这样会导致消息无序消息时序非常好通过延时队列可以指定消息的延时时间过期时间TTL等容错处理非常好通过交付重试和死信交换器DLX来处理消息处理故障伸缩一般伸缩其实没有非常智能因为即使伸缩了master queue还是只有一个负载还是只有这一个master queue去抗所以我理解RabbitMQ的伸缩很弱个人理解。持久化不太好没有消费的消息可以支持持久化这个是为了保证机器宕机时消息可以恢复但是消费过的消息就会被马上删除因为RabbitMQ设计时就不是为了去存储历史数据的。消息回溯支持因为消息不支持永久保存所以自然就不支持回溯。高吞吐中等因为所有的请求的执行最后都是在master queue它的这个设计导致单机性能达不到十万级的标准。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑