消息队列核心原理(三):RabbitMQ核心原理

目录:

消息队列核心原理(一):消息中间件与协议介绍
消息队列核心原理(二):Kafka核心原理
消息队列核心原理(三):RabbitMQ核心原理

RabbitMQ核心概念

之前我们说了Kafka的核心概念,其实这里大同小异,相似的地方我就简要提一下,重点是不同的地方。

  • Broker
  • Producer
  • Consumer
  • Message
    • 生产消费的内容 ,由消息头,消息体,属性配置构成
  • Queue
    • 用于存放消息的容器,对于RabbitMQ的所有消息,均需要存放在Queue里面
  • Channel信道
    • 全双工,支持多路复用,可以发布、订阅、接收消息
    • 该信道基于TCP连接,但是是虚拟连接的,是复用的TCP的连接
  • Connection连接
    • 一个封装在Socket层面的连接,理论上一个socket可以有多个Channel
  • Exchange交换机
    • 生产者将消息发送给交换机,交换机再将消息转发到一个或者多个queue中。交换机有多个,所以与queue的关系是多对多。
  • RoutingKey路由键
    • 生产者将消息发送给交换机,一般会指定RoutingKey,用来确定路由规则
    • 最大长度为255Byte
  • Binding
    • 通过将交换机和Queue绑定起来,RabbitMQ才会知道怎么分配消息。当然是根据BindingKey去路由,而这个BindingKey是提前指定的。
  • Virtual Host Machine
    • 用于隔离环境:比如测试环境,生产环境,开发环境。
    • 用于隔离业务:比如支付业务,日志业务的隔离
    • 要注意的是,每个VM数据都是不互通的。一个VM不能有相同的交换机名称和队列名称。

所以,根据上述名词的解释,可以大致理清一下流程:

生产者产生消息---->指定RoutingKey,封装Socket进行连接,可以有多个信道---->消息交给交换机,交换机通过一定的规则检查RoutingKey,与BindingKey进行匹配----->匹配成功后将消息交给Queue---->消费者进行消费

Exchange交换机

特点:

  1. 只能转发,不能存储
  2. 交换机机型有多种,与Queue的关系是多对多
  3. 交换机类型:Direct Exchange(点对点)、Fanout Exchange(广播)、Topic Exchange(主题型,最常用)、Headers Exchange(基本不用)

交换机类型介绍

  • Direct Exchange
    • 将一个队列绑定到交换机,要求消息与路由键完全匹配,假设路由键是1234,绑定建一定也是1234,无论是多、少、包含都无法匹配。
  • Fanout Exchange
    • 将队列绑定到交换机,被绑定的队列都会收到消息。消息处理最快(因为不需要check路由键)
  • Topic Exchange
    • 结合前两个交换机特点,更灵活。对路由键进行匹配。
  • Header Exchange

交换机典型使用场景

我们在处理日志的时候,常常会有ERROR,WARNING这种分类,我们可以把它们作为RoutingKey。这样对于每一条消息,我们便可以分类日志。

消息可靠性保证

RabbitMQ消息传递路径

  • 生产者–>交换机–>队列–>消费者
  • 通过两点确定消息的可靠性传递
    • 生产者到交换机:利用confirmCallback
    • 交换机到队列:利用returnCallback
  • 消息确认机制耗时,降低性能,因此不能在所有的消息上使用。在一些重要的场合使用即可。比如支付业务。

事务机制

RabbitMQ提供了事务机制,实际上这个事务机制是继承了AMQP的。就是在接收方确认收到消息的情况下再进行下一轮消息传递。

在这种情况下,消息传递后,发送方会被阻塞。源码是这样的:

try {
	channel.txSelect();
	channel.basicPublish(exchange, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
	int result = 1 / 0;
	channel.txCommit();
} catch (Exception e) {
	e.printStackTrace();
	channel.txRollback();
}

他会经过4个步骤:

  1. 打开事务。txSelect()
  2. 发送音讯。basicPublic()。这个就像你去人家家里做客,提前打电话问人家在不在家,并且简历连接。这个连接一旦因为网络或者其他原因导致了故障,那么就会抛出异常,被捕获后执行第4步,否则执行第三步。
  3. 提交消息
  4. 回滚

当我们仔细看源码的话,就会知道这个channel.txSelect()channel.txCommit()都是阻塞的,不收到对面的信息都不会往下执行。

因此,通过描述我们也发现了,这种方式实在是太降低效率了,我们在生产环境中是不用的。

此外,要说明的是,这种方法不能确认消息只发送一次。比如说在消息发送后,但是收到回复消息之前,Channel断掉了,会进行重新发送。

发送方确认机制

这里的发送方确认机制类似于我们TCP的超时重传机制。每发送一定数量的消息,就要接收对面传来的确认帧,从而判要不要重新发送消息。

这里有三种方法:串行模式、批量模式、异步模式。

  • 串行模式:每次发送一条消息后等待对面回应。效率一般,但是稳定。
    在这里插入图片描述
  • 批量模式:一次发送多条消息,等待对面回应一条。效率较高,但是不稳定。比如第一条消息就发送失败,这时候发送方还会接着发消息,直到没有收到回应。从第一条消息重新发。
    在这里插入图片描述
  • 异步模式:消息隔一段时间确认一次,也就是说消息的确认可能是逐条,也可能是批量。但是此方法需要自己实现确认接收和确认丢失后的逻辑。即确认接收后你想怎么处理,确认丢失后你又想怎么处理。这里由于逻辑的不确定,就不画图了。

这三种方法,逐条确认最慢,剩下两种差不多(理论上异步更快,实际上时间差的不是很多)。但是当第二种批量确认的方法遇见多条消息丢失的情况,可能反而要慢。比如每批消息都有一条消息丢失,那么这批消息就要重新发送。

幂等性要求

详见上一章:Kafka核心原理。

其实幂等性解决方法都类似,创建唯一key,写进redis。

当然还要看具体业务也可以用其他方法。

TTL与死信队列

TTL(time to live)指的是消息存活时间。当TTL过期的时候,队列中的消息会被清除。

RabbitMQ支持两种TTL设置方式:

  • 单消息配置TTL
  • 整个队列配置TTL

死信队列:没有被及时消费的消息存放的队列。
死信交换机:Dead Letter Exchange(DLE)当消息成为死信后,会被发到另一个交换机,这个交换机叫做死信交换机。

一般流程:生产者–>交换机–>队列–>消息过期–>死信交换机–>死信队列

成为死信队列的条件

  1. 消费者拒收消息,并且没有重新入队(requeue = false)
  2. 消费者在队列中未被消费,且超过队列或消息的本身过期时间
  3. 队列的消息长度达到极限

延迟队列

生产者在生产消息后,不希望新消息直接投递,而是等待某一个时间节点到来后投递,就是延迟队列。常用于推送定时任务,消费期限设置等。

RabbitMQ不支持延迟队列,所以我们需要用逻辑来替代。

在这里插入图片描述

RabbitMQ高可用

说来也怪,我们在讲Redis高可用的时候就是集群,但是RabbitMQ有两种集群模式,一种是普通集群,一种是镜像集群。使用场景、原理都是有差别的。

普通集群

对交换机、队列和虚拟主机的元数据进行同步。

啥意思呢?就是假设有三个节点node1, node2, node3,这三个节点的交换机一模一样,队列一模一样,虚拟主机一模一样。但是就是消息不一样。也就是说消息并不在节点间进行同步

假设生产者把消息扔给了node1,那么如果消费者连接了node1,可以直接取消息。但是消费者连接了node2,那么node2会从node1拉取数据。也就是说消费者如果连接node2,并不能直接获取数据。这样会出现什么问题?

  1. 假设node1崩溃了,那么消息不久获取不到了么?对的,除非node1开启了持久化操作,等待node1修复才可以拿到消息,但是如果node1没有开启持久化而且崩溃了,那就真的找不到消息了。
  2. 链路变长了,假设node1一下来了10w条消息,但是消费者在node2和node3这边,这样的话node1成了性能的瓶颈。

要这种模式干啥啊?又不是真正的高可用,好像没有用。但是人家存在必然有道理的。

分布式日志系统,我们可以利用这种普通集群。首先,日志没有如同支付业务一般重要;其次,多节点可能会导致日志持久化,即数据库连接压力过大。因此,这种简单的集群系统,只需要将node1节点连接数据库,其他节点的日志也会通过node1写入数据库。

镜像集群

除了普通集群同步的数据外,还将消息进行同步。适用于消息过多的情形。

这种镜像集群的方式更像Redis的集群模式,节点分为master节点和slave节点。几乎所有的操作都是通过master节点进行的。我加粗的目的是告诉大家,并不是所有操作都只和master交互,我们一会说这一点。

请看文章:https://blog.csdn.net/weixin_40816738/article/details/105704335(主要看人家的图,镜像队列的结构,太麻烦我懒得画了)

在这里插入图片描述

图有点乱,但是根据序号捋一遍就知道大概流程,像这种消息的发送,是一次性发送给所有的节点,因此不怕在消息发送过程中的主节点挂掉。消费者消费之后,master会广播消息被消费,让slave做出相应的动作。这是一个原子化的操作。

节点加入

新加入的节点是不会被选举成master的,master节点只会在老节点中选举。就好比未满18岁的孩子没有被选举权一样。当然,在加入集群前,新的节点内容会完全清空,之后再进行其他同步操作。

没有真正的高可用

假设三个节点,master挂掉,选举slave1成为新的master。在master恢复前,slave1挂掉,slave2成为master。在二者恢复后,数据同步前,slave2挂掉,数据就找不回来了。

假设三个节点,slave相继挂掉后恢复,同步数据前master挂掉,同理,数据也找不回来的。

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那我就换个名字吧

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值