1. RabbitMQ的事务机制
RabbitMQ支持的事务,是保证消息发送的原子性。
它允许发布者在一个事务内发布多条消息,并通过提交和回滚确保所有消息的原子性操作,这表示要么所有消息都成功发布,要么没有消息被发布。
使用方式和数据库的事务方式很像
- 开始事务
- 发布消息
- 提交事务(如果成功)回滚事务(如果失败)
需要注意,事务消息的性能比较差
,因为一开始事务需要请求broker并等待broker的响应,确认没问题后,再发送消息,且后续的提交事务也需要等待broker的确认。这个操作是同步的所以性能比较低。
一般采用confirm模式替代,有批量confirm模式和异步confirm模式。
2. RabbitMQ的routing key和binding key的最大长度是多少字节
RabbitMQ的routing key和binding key的最大长度是255字节,超过这个长度的键会被RabbitMQ拒绝。
- Routing Key:生产者发送消息时附带的一个键,用于交换机根据该键将消息路由到相应的队列
- binding key:在队列和交换机绑定时指定的键,用于定义交换机如何根据路由键将消息路由到队列。
3. RabbitMQ如何保证消息的顺序性
RabbitMQ如果要保证消息的顺序性,首先生产者需要 1.按序发送
,确认上一条消息发送成功后再发送下一条。然后 2.需要使得这类消息仅发送至一个队列
,且 3. 只有一个消费者消费这个队列
。4. 消费者必须消费完这条消息后,在消费另一条消息
,不能是多线程并发消费,这样才能保证消息的顺序性。
如果要全局消息顺序,那么全局就只使用一个队列。
4. RabbitMQ的工作模式
RabbitMQ实际上可以分为七种模式
-
简单模式
作用:生产者将消息发送到队列,消费者从队列中获取消息,每条消息只能被一个消费者消费。 -
工作队列模式
作用:通过多个消费者分担任务,达到负载均衡的目的。消息在多个消费者之间分配,每条消息只能被一个消费者处理。
-
发布/订阅模式
作用:消息发布到交换机,所有绑定到交换机的队列都会收到消息,常用的交换机类型是fanout。
适用场景:适用于广播消息的场景,如日志处理、事件通知等
-
路由模式
作用:消息发布到交换机,并根据路由键将消息发送到相应的队列,常用的交换机类型是direct。
-
主题模式
作用:消息发布到交换机,并根据路由键模式(通配符匹配)将消息发送到相应的队列,常用的交换机类型是topic。
-
RPC模式
作用:实现远程调用,客户端发送请求消息到队列,服务器端处理后返回响应消息。 -
发布确认模式
作用:用于确保消息已经成功发布并被broker接收。
5. RabbitMQ的缺点
- 性能问题
- 当队列中堆积了大量消息时,RabbitMQ的性能会急剧下降。因为他需要处理和存储这些消息,尤其是在内存和磁盘资源有限的情况下,性能影响更为明显。
- 相比kafka和rocketMQ,他的性能通常是最差的。
-
开发语言限制
RabbitMQ用Erlang语言开发 -
功能性不足
相比Kafka和RocketMQ,RabbitMQ在一些高级功能上可能不如他们丰富。
6. RabbitMQ中,如何保证消息的持久化
RabbitMQ的持久化分为 队列/交换机的持久化
和 消息的持久化
两种。
- 将队列和交换机的 Durable 设置为True。这样创建的队列和交换机都是持久化的,即RabbitMQ服务器重启后,队列和交换机的元数据会被保留。
- 消息的持久化是在生产者发送消息时,将Delivery mode设置为2,表示会持久化消息,这样的消息在发送到持久化队列后,服务器重启后消息也还存在着。
Delivery Mode扩展
- 默认为1,表示非持久化消息,当消息发送至broker,不会将其写入磁盘,如果消费者还未消费到这条消息,broker宕机了,那么消息就丢失了。
- 参数设置为2,表示持久化消息,会写入到磁盘,但是这个过程实际上是异步的,所以正常情况下即使broker宕机了,也有可能丢消息,只是能保证大部分消息的持久化。
7. RabbitMQ中的Channel是什么
RabbitMQ在网络层有Connection 和 Channel两个概念。
Connection其实对应的就是一条TCP连接,而Channel是Connection中的虚拟连接,一个Connection可以创建多个虚拟连接。
在RabbitMQ中,客户端和Broker之间的通信是基于Channel的,这样可以减少实际的TCP连接数,节省系统资源使用。
8. RabbitMQ如何保证高可用
首先,如果要实现RabbitMQ的高可用,单机部署肯定是不合适的,因为单机故障,消息必然无法发送和消费。
因此需要引入集群模式,而集群主要分为三种:
- 标准集群
- 镜像集群
- 联邦集群
1. 标准集群(每个节点只负责一部分数据)
多台RabbitMQ通过网络组成一个集群,所有节点共享元数据(如队列、交换机、绑定等),但一个消息仅存储在单个节点上
。
客户端可以连接到集群中的任意节点,因为元数据是共享的,所以任何一个节点都知晓具体消息在哪个节点上,因此可以实现负载均衡。但消息仅会存储在一个节点上不会自动复制到其他节点,所以如果某个节点故障,那么实际上这部分消息就无法消费了,但别的节点还可以提供服务。
2. 镜像集群模式(队列的副本会同步到其他节点上)
相比于标准模式,镜像队列模式下,队列的主副本和一个或多个副本会同步到不同的节点上,即队列的消息会复制到多个节点,如果主队列所在的节点故障,副本节点会自动接管,保证队列的可用性。
3. Fedrated集群模式(集群之间的数据异地复制)
这其实就是多活模式,主要用于异地的数据复制,例如,在北京上海两个机房都部署RabbitMQ集群,他们之间依赖的RabbitMQ的federation插件实现持续的可靠的AMQP数据通信。
如果北京机房出了故障,上海的机房可以顶上。
9. 为什么RocketMQ不使用Zookeeper作为注册中心,而选择自己实现NameServer
- 设计简洁且专用:RocketMQ的NameServer设计简单,专门用于RocketMQ的需求,相比于通用的Zookeeper,NameServer更加轻量级且易于部署和维护。
- 高可用性:RocketMQ的NameServer是无状态的,多个NameServer之间实例对等,可以通过DNS进行负载均衡,而Zookeeper是一个强一致性的系统,对节点之间的同步有严格要求,在某些极端情况下(如网络分区),zookeeper的可用性可能受到影响。
- 性能优化:NameServer只处理简单的配置和路由消息,不涉及复杂的状态同步和一致性协议,相比于Zookeeper性能更高。
- 降低依赖性:使用自研的NameServer可以降低对外部系统的依赖。
10. Kafka高性能的原因
- 顺序写:磁盘顺序写的性能远远大于随机写的性能
- 页缓存:这个主要使用了linux系统底层的一个机制,即Page Cache,又称页缓存,当消息写入到Page Cache后立刻返回,然后等到系统的刷盘进程进行刷盘操作后,页缓存将对应的内容一次性写入磁盘。
之所以快,是因为我们写入内存的速度远远大于写入磁盘的速度,然后我们写入内存之后,再将内存中的东西再
批量一次性写入磁盘
,这个过程就是聚集写的过程,从一定程度上减少了IO的次数。
- Kafka支持批量接受和发送消息:并且支持消息在压缩之后进行接收和发送,这样从一定程度上减少了网络传输的次数和负担,提高了Kafka消息读取以及消息进行网络传输的效率。
- 零拷贝:
Kafka的Broker传递数据给消费者的过程使用了零拷贝,底层主要使用了sendfile系统调用,减少了系统用户态和内核态的上下文切换以及数据拷贝,从而提高Kafka数据拷贝的性能。
- Kafka采用分段与索引的策略来提高性能,即利用偏移量和时间索引文件实现快速消息查找,从而提高Kafka消息查找的效率。