RocketMQ知识点整理

前言


本文旨在总结和整理在学习RocketMQ中遇到的零碎问题,方便之后的回顾。

一、RocketMQ如何保证高可用

高可用问题在之前的文章中已经从源码层面详细分析过一遍了,具体可以看之前的博客,这边只是做一个总结。

高可用在RocketMQ的每个角色中都有体现。首先,从Producer出发,每一个消息都有一个Topic,每一个Topic都对应着多个Broker,因此即使一个Broker组的Master不可用了,也能选择别的Broker进行消息的发送,保证消息生产的高可用。

其次,从Broker出发,高可用主要体现在主从同步上,一个Broker集群拥有多个Broker分组,每个Broker分组包含两类的服务器,一类是Master主节点,只允许有一个,另一类是Slave,允许有多个从节点。Master结点只提供读写的服务,Slave从结点只提供的服务。每个Broker分组内Master会和Slave进行通信和数据同步,但是每个Broker分组之间不进行通信,主从同步不具备主从切换功能,当主节点宕机以后,从结点不会接管消息的接收,但可以提供消息的读取。

再次,从Consumer出发,消息消费者提供了负载均衡机制,并且在消费的时候能进行主从选择,当主结点处于繁忙或者宕机的状态时,消息消费者能切换到从节点进行消息消费。

最后从NameServer出发,NameServer本身就是一个集群,集群中每台服务器中数据都是一致的,并且互不影响,从而达到NameServer的高可用。

二、RocketMQ如何保证高吞吐

首先,提高消息生产者Producer的发送速度。

其次,能在Broker端进行消息的过滤,可以减少无效消息发送到Consumer,少占用网络带宽从而提高吞吐量,过滤主要有三种方式:用消息的Tag或者Key过滤、用SQL表达式的方式进行过滤和用Filter Server方式进行过滤。

再次,提高了Consumer的消息处理能力,这边主要从增加Consumer实例或者提高单个实例的线程数量来提高消息并行度,在某些业务场景下能用批量的方式进行消息的消费,还能检测延时情况,选择性的丢弃不重要的消息来提高消息消费的速度。

最后,还有消息消费时候的负载均衡机制也能保证消息的高吞吐。

详细可以参考这篇博客

三、RocketMQ和kafka的区别

我认为最主要的区别有两点,一点是数据可靠性,另一点是性能的区别。

数据可靠性:RocketMQ支持异步刷盘、同步刷盘、同步复制和异步复制。而Kafka只支持异步刷盘和异步复制。所以RocketMQ的同步刷盘在单机可靠性上比Kafka更高,不会造成数据丢失,而且RocketMQ的同步复制也比Kafka的异步复制更可靠,Kafka若主机宕机,备机会自动切换,由于是异步复制,那么很可能数据会有丢失,并且当Leader重启后,由于可能已经消费过,数据也可能存在冲突。但是RocketMQ同步复制是不存在这个问题的。

性能:Kafka单机写入的TPS在百万条/秒,消息大小为10个字节。RocketMQ单机写入TPS单实例约7万条/秒。但是Kafka是将消息堆积起来一起发送,从而减少网络的IO,但是若此时Producer宕机,会导致消息丢失,业务出错。所以RocketMQ并不是采用这种方式,原因有以下几点:第一点是Producer通常是使用Java语言的,缓存过多的消息会导致GC。第二点,如果宕机,会造成消息的丢失,从而导致业务出错。第三点,Producer通常为分布式系统,且每台机器都是多线程发送的,认为线上单个Producer每秒产生的数据量有限,不可能上万。第四,缓存的功能完全可以由上层业务完成。

其他的一些对比可以参照下面的博客:

https://www.cnblogs.com/ynyhl/p/11320797.html

四、RocketMQ的消息是否有序

在RocketMQ中消息分为全局顺序消息和局部顺序消息,而全局顺序消息要求某个Topic下所有消息都要保证消息与产生顺序相同;部分顺序消息是指,只要保证每一组消息被顺序消费即可。比如之前在RocketMQ中举的那个例子。如果要保证,全局顺序消息的话,只需要把生产者和消费者设置为单线程即可,这样一来,就要牺牲高并发和高吞吐,所以一般业务也只需要部分顺序消息即可。

五、RocketMQ的消息局部顺序是如何保证

要保证消息局部顺序只需要保证两件事。首先在消息发送的时候,消息的生产者需要把同一个小组的消息发送到同一个Message Queue中。其次在消费的时候,只用一个线程去处理这个队列中的消息即可。

那么如何保证一个队列只被一个消费者消费且如何保证一个消费者中只有一个线程能进行消费

从源码的角度分析一下

首先,顺序消息在创建消息队列拉取任务时需要在Broker服务器锁定该消息队列。

其次,创建消息拉取任务时,消息客户端向broker端申请锁定MessageQueue,使得一个MessageQueue同一个时刻只能被一个消费客户端消费

然后,消息消费时,多线程针对同一个消息队列的消费先尝试使用synchronized申请独占锁,加锁成功才能进行消费,使得一个MessageQueue同一个时刻只能被一个消费客户端中一个线程消费

参考:https://blog.csdn.net/hosaos/article/details/90675978

六、RocketMQ事务消息机制如何实现

总计来说,有这么几步

1、发送消息(这里是half消息)。

2、服务端相应消息结果。

3、根据发送结果执行本地事务。

4、根据本地事务状态执行Commit或者Rollback。

5、对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”。

6、Producer收到回查消息,检查回查消息对应的本地事务的状态。

7、根据本地事务状态,重新Commit或者Rollback。

可参考

https://blog.csdn.net/zhangcongyi420/article/details/90582303

七、重复消费问题的解决

RocketMQ本身不能保证消息的重复消费问题,这个问题只能在业务系统端进行解决,比如设置唯一的id号,让已经消费过的消息不再被消费即可或者消费端处理消息的业务逻辑保证幂等性。

八、RocketMQ的Push或者Pull

push或者pull本质都是pull模型,即Consumer轮询从Broker拉取消息。RocketMQ中并没有真正实现推模式,而是消费者主动向消息服务器拉取消息,RocketMQ推模式是循环向消息服务端发送消息拉取请求。

pull是指消费者主动去Broker拉取,push是指主动推送给Broker。

push实时性高,但增加服务器负载,消费端能力不同,如果push的速度较快,消费端会出现问题。

pull是消费者掌握主动权,可控性较好,但时间间隔不好设置,间隔太短则请求太多,造成忙等的状态,浪费资源,间隔太长了,消息就不能及时处理。

pull和push都有自己的优缺点,后面就采用了长轮询的方式,兼顾push和pull。

如果不启用长轮询机制,则会在服务端等待一段时间后(挂起)再去判断消息是否已经到达消息队列,如果消息未到达则提示消息拉取客户端消息不存在。

如果开启了长轮询,即Client发送消息请求,Server端接受请求,如果发现Server队列里没有新消息,Server端不立即返回,而是持有这个请求一段时间(通过设置超时时间来实现),在这段时间内轮询Server队列内是否有新的消息,如果有新消息,就利用现有的连接返回消息给消费者;如果这段时间内没有新消息进入队列,则返回空。