1. RocketMq的组成及各自的作用?
-
在RocketMq中有四个部分组成,分别是Producer,Consumer,Broker,以及NameServer,类比于生活中的邮局,分别是发信者,收信者,负责暂存,传输的邮局,以及协调各个地方邮局的管理机构。
-
NameServer:
主要是 Topic 和 Broker 注册中心,支持 Broker 动态注册和发现,主要保存 Topic的路由信息和Broker的状态信息。通常也是集群部署,但是各 NameServer 之间不会互相通信, 各 NameServer 都有完整的路由信息,即无状态(跟zk的区别是,zk为有状态的)。 -
Broker:
就是MQ本身,负责收发消息、持久化消息,每个broker负责管理一部分topic的消息;主要分为 Master Broker 和 Slave Broker,一个 Master Broker 可以对应多个 Slave Broker,但是一个 Slave Broker 只能对应一个 Master Broker。Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与Name Server集群中的所有节点建立长连接,定时注册Topic信息到所有Name Server。Broker 启动后需要完成一次将自己注册至 Name Server 的操作。 -
Producer:消息生产者
可以集群部署。它会先和 NameServer 集群中的随机一台建立长连接,得知当前要发送的 Topic 存在哪台 Broker Master上,然后再与其建立长连接,支持多种负载平衡模式发送消息。 -
Consumer:消息消费者
可以集群部署。它也会先和 NameServer 集群中的随机一台建立长连接,得知当前要消息的 Topic 存在哪台 Broker Master、Slave上,然后它们建立长连接,支持集群消费和广播消费消息。 -
Topic:
标识一类消息的逻辑名字,消息的逻辑管理单位。无论消息是生产还是消费都需要指定Topic;比如电商系统可以分为:交易消息、物流消息,每条消息都必须有一个topic,一个类型的消息可以定义一个topic,也可以定义多个,根据业务需求来定; -
Tag:
可以看作子主题,它是消息的第二级类型,同一业务模块不同目的的消息就可以用相同 Topic 而不同的 Tag 来标识。比如交易消息又可以分为:交易创建消息、交易完成消息等,一条消息可以没有 Tag ;虽然同一个topic的管理逻辑一样,但是消费topic1的时候,如果你订阅指定的是tagA,那么tagB的消息不会投递。 -
Group:
分组,一个组可以订阅多个Topic,代表某一类的生产者和消费者,一般来说同一个服务可以做为Group,同一个Group一般来说发送和消费的消息都是一样的。 -
Queue:
队列其实就是对Topic的分片,在Kafka里面就是Partition。将Topic分片再切分为若干等分,其中的一份就是一个Queue。每个Topic分片等分的Queue的数量可以不同,由用户在创建Topic时指定。这些队列会被RocketMQ均衡的分布在不同Broker上,Producer在发送消息时会根据一定策略选择一个消息队列进行发送,这样就可以实现负载均衡和提高吞吐的效果。 -
Offset:
偏移,本质上有两种Offset,一种是写入时末尾的Offset,另一种是同一消费组读的Offset。RocketMq消息内容是分片存储,CommitLog 的大小默认是1G,当超过大小限制的时候需要准备新的文件,CommitLog 采用混合型存储,也就是所有 Topic 都存在一起,顺序追加写入,文件名用起始偏移量命名。其次RocketMq还存储了消息体与偏移的关系,用于快速随机读取和检索。
2. 为什么使用消息队列?
最开始的时候业务量小,直接单机能解决,后来业务量大,采用微服务的设计思想,分布式部署的方式,拆出了很多服务,场景也就越来越复杂,所以引用了消息队列。
3. 为什么要用RocketMq?
削峰:在高并发场景下,系统的请求量可能会瞬间增加,给服务器带来巨大的压力。通过使用消息队列,可以将突发的大量请求进行缓冲和削峰,使其平滑地处理,从而避免服务器过载和崩溃;
异步:如果下单流程涉及多个系统,影响支付时间,所以支付同时完成其他工作去校验;
解耦:可以将系统的各个模块解耦,减少模块之间的直接依赖。这样可以使各个模块独立地进行开发和部署,提高系统的可扩展性和灵活性。同时,解耦也可以降低系统间的耦合度,提高系统的可维护性和稳定性;
限流:通过控制消息的生产者和消费者的速度,可以限制系统的流量,防止过多的请求对服务器造成过大的压力。
4. RocketMq的优点?
- 单机吞吐量:十万级别;
- 可用性:分布式架构,可用性强;
- 消息可靠性:MQ的功能较为完善,还是分布式的扩展性好;
- 支持10亿级别的消息堆积,不会因为堆积的性能下降;
- Java源码,可二次开发;
- 适用于可靠性要求要求高的场景;
- 稳定性搞,经过阿里系统的考验;
5. RocketMq的缺点?
- 支持语言不多:Java和C++(不成熟);
- 移植复杂:没有引入JMS等接口,系统迁移需要改大量代码;
- 系统可用性降低 :系统引入的外部依赖越多,越容易挂掉。万一 MQ 挂了,MQ 一挂,整套系统崩 溃,你不就完了?
- 系统复杂度提高 :硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况? 怎么保证消息传递的顺序性?问题一大堆。
- 一致性问题 :A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致 了。
6. RocketMq的顺序消费?
不能保证全局顺序消费,只能保证单个queue里的顺序,queue是典型的FIFO;
7. RocketMq的重复消费以及怎么解决?
影响重复消费的主要原因是网络原如下:
- 生产者没有接受到消费者的结果;
- 消费成功没有返回ack;
- 消费成功但是宕机了;
解决办法:
RocketMQ重复消费的问题可以通过以下几种方式解决:
- 分布式锁:在生产环境中,通常都是多元jvm集群环境,当RocketMQ系统投递重复消息时,可能会造成数据库重复消费。此时可以采用分布式锁,通过zookeeper和redis搭建,解决消息重复的甄别问题。
- 数据库约束+java异常处理机制:首先需要针对数据库简历约束,不允许产生重复数据,然后再使用java的异常处理机制来规避重复消息。
- 幂等性处理:消费者端要自己保证消费的幂等性,例如消费者收到消息后,从消息中获取消息标识写入到Redis或数据库,当再次收到该消息时就不作处理。
- 负载均衡阶段重复听或漏听的问题:微众银行在负载均衡结果变化过程增加了一个过渡态,在过渡态的时候,Consumer会继续保留上一次负载均衡的结果,直到原消费者拉取的消息全部ack,才释放老的结果。改造的实现是在RocketMQ的Broker端增加了一个ConsumeQueueAccessLockManager类,对Queue加了锁。
8. RocketMq的消息丢失
Producer端:
利用自带的事务处理机制,发送half信息,如果正常就处理,不正常就回滚;
Broker端:
- 没来得及持久化,由异步刷盘改为同步刷盘;
- 集群部署,主从模式,高可用;
Consumer端: - 消费完成后,手动确认ack;
9. RocketMq的消费失败
- 默认重试16次(会导致重复消费的问题),否则会进入死信队列;
- 死信解决办法:让mq重新发消息,然后消费消息,看问题所在;
- 死信队列三天后会自动删除,查看死信队列可视化工具-rocket mq console
10. RocketMq的消息堆积
- consumer端增加消费者,扩容;
- 熔断隔离:一个Broker积压将其熔断隔离,发送其他队列
- broker:扩容
- producer:限流
11. RocketMq的延时消息
-发送消息不会被及时消费,比如关闭超时未支付订单,下单30分钟后支付启动定时,隔几秒去扫描待支付的订单
12. RocketMq Broker 中的消息被消费后会立即删除么?那么消息会堆积么?
- 不会,每条消息都会持久化到commitLog中,每个Consumer连接到Broker后会维持消费进度信息,当有消息消费后只是当前Consumer的消费进度(commitLog的offset)更新了;
- 默认72小时后会删除不再使用的Commitlog文件;
13. RocketMq什么时候清理过期消息?
- 消息文件过期(默认72小时)且到达清理时间点(默认是凌晨4点)后,会自动清理过期文件。
- 消息文件过期(默认72小时)且磁盘空间占用率已达过期清理警戒线(默认75%)后,无论是否达到清理时间点,都会自动清理过期文件。
- 磁盘占用率达到清理警戒线(默认85%)后,开始按照设定好的规则清理文件,无论是否过期。
14. RocketMq消费模式有几种?
消费模型由Consummer决定,消费维度为topic
集群消费:默认的模式
- 一条消息只会被同Group中的一个Consumer消费;
- 多个Group同时消费一个Topic时,每个Group都会有一个Consumer消费到数据;
- 根据队列分配策略算法,每个Consumer实例只会消费自己队列上的数据,消费完的消息不会被其他实例消费。这种模式适用于大部分消息业务,能够提高系统的稳定性和可用性
广播消费:同Topic下的消息会被多个实例共同消费。也就是说,如果一个Topic下有多个Consumer实例,那么每个消息会被所有的Consumer实例消费一次。这种模式适用于一些分发消息的场景,例如将消息同时发送给多个消费者进行处理。
15. RocketMq消息消费是push还是pull?
没有真正意义的push,都是pull,虽然有push类,但是实际底层是长轮训机制,即拉取方式;
16. RocketMq为什么是主动拉取而不是监听的方式?
- 事件驱动方式是建立好长连接,由事件(发送数据)的方式来实时推送。
- 如果 broker 主动推送消息的话有可能 push 速度快,消费速度慢的情况,那么就会造成消息在 consumer 端堆积过多,同时又不能被其他 consumer 消费的情况。
- 而 pul 的方式可以根据当前自身情况来 pull,不会造成过多的压力而造成瓶颈。所以采取了 pull 的方式。
17. RocketMq Broker是如何拉取请求的?
RocketMQ的Broker在处理拉取请求时,会根据负载均衡策略选择一个负责处理该请求的Consumer实例。Broker将消息队列中存储的消息按照提交偏移量的顺序发送给Consumer实例。Consumer实例接收到消息后,会根据消费策略对消息进行处理,并将处理结果返回给Broker。Broker收到Consumer的处理结果后,会更新消息的消费状态,以便下次拉取时能够正确地发送。
18. RocketMq是如何做到负载均衡的?
Producer端:发送端指定message queue发送消息到相应的broker,来达到写入时的负载均衡。同时,Producer还支持开启隔离机制,通过判断Broker是否被隔离以及是否是上一次选择的那个Broker,来避免将消息发送到不可用的Broker上。
Consumer端:默认情况下,Consumer会根据队列的平均值进行负载均衡,即将所有队列的平均值作为Broker的负载均衡值。如果某个Broker的负载均衡值高于其他Broker,那么Consumer会优先选择该Broker进行拉取请求。如果多个Broker的负载均衡值相同,则会根据优先级选择。
其他负载均衡算法:
- 环形分配策略
- 手动配置分配策略
- 机房分配策略
- 一致性哈希分配策略
- 靠近机房策略
19. 当RocketMq的负载均衡consumer和queue不对等的时候会发生什么?
- 如果consumer<queue的个数,会存在部分consumer消费多个queue的情况;
- 如果consumer=queue的个数,一个consumer消费一个queue;
- 如果consumer>queue的个数,部分consummer空余,会浪费。
20. RocketMq如何保证数据的高容错性?
- 首先,RocketMQ采用消息持久化机制,将消息持久化到磁盘上,并通过刷盘机制保证数据不丢失。
- 同时,RocketMQ还提供同步刷盘和异步刷盘两种方式,以满足不同场景的需求。
- 其次,RocketMQ采用主从复制的方式来提高系统的可用性。对于每个队列,RocketMQ会为其创建一个主节点和多个从节点。主节点负责消息的写入,从节点负责消息的复制和读取。当主节点出现故障时,从节点会接替主节点的工作,确保消息的正常处理。
- 此外,RocketMQ还具备故障恢复的能力。当RocketMQ服务端发生故障或宕机时,可以通过重启服务端来恢复。重启服务端后,RocketMQ会自动从上次的快照中恢复未被消费的消息,并继续处理后续消息。
- 同时,RocketMQ还支持消息的重试机制。当消息发送失败时,RocketMQ会根据预设的重试次数和时间间隔进行消息的重试,直到消息发送成功或达到最大重试次数。这样可以保证消息的可靠传输,提高系统的容错性。
- 最后,RocketMQ还通常使用自动的降级熔断策略,当性能达到阈值时就会自动开启。
- 此外还会设置一个手动的降级开关,来人工开启降级流程。这样可以保证在面对故障或性能问题时,RocketMQ能够自动或手动地进行降级处理,保障服务的可用性和稳定性。
21. RocketMq任何一台Broker突然宕机了怎么办?
-
RocketMQ的Broker采用主从架构和多副本策略来解决Broker突然宕机的问题。具体来说,Master收到消息后会同步给Slave,这样一条消息就不止一份了,Master宕机了还有Slave中的消息可用,保证了MQ的可靠性和高可用性。
-
如果Broker宕机,NameServer会感知到。Broker会定时向NameServer发送心跳,然后NameServer会定时运行一个任务,去检查一下各个Broker的最近一次心跳时间。如果某个Broker超过120s都没发送心跳了,那么就认为这个Broker已经挂掉了。
22. RocketMq的Broker会把信息注册到哪个NameServer上?
Broker启动的时候会向所有的NameServer节点进行注册,注意这里是向集群中所有的NameServer节点注册,而不是只向其中的某些节点注册,因为NameServer每个节点都是对等的,所以Broker需要向每一个节点进行注册,这样每一个节点都会有一份Broker的注册信息。
23. RocketMq的自动伸缩扩容的机制是什么?
-
RocketMQ的自动伸缩扩容机制主要基于Broker的负载情况。当Broker的负载过高时,会自动触发扩容操作,增加新的Broker实例来分担负载。扩容过程不需要人工干预,系统会自动完成。同时,当流量回归正常后,为了防止资源的浪费,可以自动缩容,将一些不必要的Broker实例移除。
-
此外,RocketMQ还支持消息队列的动态扩容和缩容。在Topic的消息量特别大时,可以通过增加消息队列的数量来提高系统的处理能力。相反,如果消息队列的数量过多,也可以适当减少,以节约系统资源。
-
RocketMQ的自动伸缩扩容机制可以有效地提高系统的可用性和稳定性,同时避免了资源的浪费。在实际应用中,需要根据具体的需求和场景进行配置和调整,以达到最佳的效果。
24. RocketMq与Kafka的区别?
- R支持定时重试,K消费失败不支持;
- 都支持顺序消费,但是K宕机,消息乱序;
- R支持定时消息、分布式消息、消息查询,K都不支持
- R编写用Java,K用scale
- R定位于非日志可靠消息传输,K主要定位于日志传输
- R支持异步/同步刷盘,K支持异步实时刷盘
25. 让你手动来实现一个分布式消息中间件,整体架构你会如何设计?
设计一个分布式消息中间件需要考虑多个方面,包括消息的可靠性、一致性、性能、扩展性、容错性等。以下是一个可能的整体架构设计:
- 消息队列服务(Message Queue Service):这是整个分布式消息中间件的核心,负责接收、存储和转发消息。它可以由多个节点组成,以实现高可用性和可扩展性。
- 节点管理服务(Node Management Service):负责管理消息队列服务的节点,包括节点的添加、删除、监控等。它需要保证在节点故障时能够及时地发现并处理,以保证系统的稳定性和可用性。
- 客户端代理服务(Client Proxy Service):提供给客户端使用的代理服务,负责与消息队列服务进行交互。客户端通过这个服务来发送和接收消息,而不需要直接与消息队列服务的节点进行通信。这样可以减少客户端与服务器之间的网络开销,提高系统的性能和稳定性。
- 监控与日志服务(Monitoring and Logging Service):负责收集、处理和存储消息队列服务的运行状态和日志信息。通过这个服务,可以实时监控系统的运行状态,及时发现和处理问题,保证系统的稳定性和可靠性。
- 配置管理服务(Configuration Management Service):负责管理消息队列服务的配置信息,包括节点配置、队列配置、订阅关系配置等。通过这个服务,可以方便地修改和配置消息队列服务的各项参数,以满足不同的业务需求。
- 安全与认证服务(Security and Authentication Service):负责提供安全和认证功能,包括用户身份验证、访问控制、加密解密等。通过这个服务,可以保证消息的安全性和隐私性,防止敏感信息的泄露和未经授权的访问。