Producer : 消息生产者,生产者的作用就是将消息发送到 MQ。
Consumer : 消息消费者,消费 MQ 上的消息的应用程序
Consumer Group : 消费者组,消费同一类消息的多个 consumer 实例组成一个消费者组。
Topic : 消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,就用不同的Topic进行分类。
Offset : 偏移量,记录消费者已消费消息的位置。
Message : 是消息的载体。一个 Message 必须指定 topic。Message 还有一个可选的 tag 设置,以便消费端可以基于 tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 broker 上的消息,方便在开发过程中诊断问题。
Tag : 标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。
Broker : RocketMQ 系统的主要角色,其实就是前面一直说的 MQ。Broker 接收来自生产者的消息,储存以及为消费者拉取消息的请求做好准备。
Name Server : 为 producer 和 consumer 提供路由信息并协调管理borker。
我们对着下面的架构图简单来说一下消息发送到消息消费的整个流程。多个producer发送消息,发送到broker上,多个消费者接受消息,接受到消息后进行消费,整个流程比较简单。NameServer保存borker,主题等信息。
两种消息发送方式:
1.同步发送:发送成功后继续执行代码逻辑。
2.异步发送:发送后,不管成功失败执行代码逻辑。成功后调用回调方法。
两种刷盘方式,flushDiskType配置,SYNC_FLUSH,ASYNC_FLUSH。
1.同步刷盘方式:消息写入磁盘后再返回成功状态。
2.异步刷盘方式:消息写入内存后就返回成功状态。
两种复制方式,表示消息从Master复制到Slave的方式,brokerRole,ASYNC_MASTER(异步master),SYNC_MASTER(同步master),SLAVE(slave)。
1.同步复制方式:等Master和Slave都写入成功后才返回写入成功。
2.异步复制方式:Master写入成功后就返回写入成功。
三种消费方式 :
1.Push(服务端主动推送消息),RocketMQ服务器收到消息后自动调用消费者函数来处理消息,自动维护Offset。支持两种消息模式,Clustering模式,同一个ConsumerGroup的每个Consumer消费订阅消息的一部分内容,broadcasting模式,同一个ConsumerGroup的每个Consumer都消费所订阅的的全部消息。
2.Pull (客户端主动拉取消息),Client端循环从Server端拉取消息。需要客户端自己维护Offset。
3.长轮询消费方式,Client发送消息请求,Server端接受请求,如果发现Server队列里没有新消息,Server端不立即返回,而是持有这个请求一段时间(通过设置超时时间来实现),在这段时间内轮询Server队列内是否有新的消息,如果有新消息,就利用现有的连接返回消息给消费者;如果这段时间内没有新消息进入队列,则返回空。
深入了解了上面三个角色,我们来总结下双master,双slave模式下的整个发送,消费流程。生产者发送消息,消息会负载均衡到两个Master上,如果master的刷盘方式是同步刷盘方式,复制方式是同步复制方式,需要消息写到master和slave的硬盘上后,服务器才会放回发送消息成功。消息存储到服务器后,消费者根据自己的消费方式来消费消息,如果是Push,消息到达服务器后马上推送消息到消费者,如果是pull,消费拉取消息后再消费。
每一个Topic默认会创建多个队列,消息发送默认是会采用轮询的方式发送到不通的MQ,而消费端消费的时候,是会分配到多个queue的,多个queue是同时拉取提交消费,多个消费者同时消费多个MQ。如果要保证顺序消息只要满足下面两点:
1.producer端保证发送消息有序,且发送到同一个队列。
2.consumer端保证消费同一个队列。
消息发送到同一个队列,和消费者消费同一个队列的消息这两个条件比较好满足。保证producer端发送消息的有序,这点需要我们应用自己控制,消息发送有重试机制,在网络不稳定的情况下,第一个消息发送失败,在没有收到服务器发送成功的消息之前,是不能发送第二个消息的,不然会导致消息无序。
保证消息不丢失从三个角度来考虑
1.producer,通过同步的方式阻塞式的发送,check SendStatus,状态是OK,表示消息一定成功的投递到了Broker,状态超时或者失败,则会触发默认的2次重试。如果重试2次后还是失败,我们记录发送失败的LOG。
2.broker,集群需要多个master,每个master都有slave,主从同步方式设置为同步复制,刷盘方式这是为同步刷盘方式。最大程度上消除单点故障。
3.customer,如果是push模式,集群无单点故障的情况下,offset不会出现问题。如果是pull模式,客户端自己维护offset,消费者在消费成功一条消息后,更新自己的offset,保证消费消息offset的准确性。如果某条消息出现异常,则记录下来,或者存储后重新消费。
RocketMQ不保证消息的不重复消费,这个需要我们自己来保证,一般有两个方式:
1.幂等性,消息消费多次不影响最终结果。
2.唯一性键记录,每个消息有一个唯一性的ID,消费过的消息记录一下,每次消费消息之前进行对比。
RocketMQ基于二阶段提交方式来实现事务消息,可以保证消息发送和本地事件逻辑同时成功和失败。我们看下面的步骤:
RocketMQ使用Netty做底层通信,协议了编码器解码器自己设计的。Netty不了解可以看我这两篇文章https://blog.csdn.net/moranzi1/article/details/87905308,https://blog.csdn.net/moranzi1/article/details/88323991
1.顺序写入
因为硬盘是机械结构,每次读写都会寻址->写入,其中寻址是一个“机械动作”,它是最耗时的。所以硬盘最“讨厌”随机I/O,最喜欢顺序I/O。为了提高读写硬盘的速度,RocketMQ使用顺序I/O。
2.零拷贝技术
使用MappedByteBuffer来实现数据的复制,减少复制次数,提供速度。
master挂了的情况下,RocketMQ不支持把slave转换为master。所以没使用zookeeper而简单的使用nameserver。所以搭建RocketMQ集群至少要使用两个master,防止一个master挂了,不能发送消息到RocketMQ集群。
语言差异:kafka使用Scala,RocketMQ使用java
架构差异:
在kafka里一台服务器对应一个broker,一个broker对应一个首领分区(master)和多个分区副本(slave),一个topic对应多个首领分区(master)。在rockeMQ里,一台服务器对应一个broker,broker分为master和slave,一个topic对应多个master。
在Kafka里面,Maser/Slave是选举出来的,在RocketMQ里,不需要选举,一台服务器启动的时候就需要设置为Master还是Slave(所以RocketMQ不需要zk做选举)。
通信差异:kafka自己实现的网络通信,RocketMQ基于Netty实现网络通信。
存储差异:kafka每一个分区或者分区副本都对应一个单独的物理文件。RocketMQ一个broker(一个服务器)对应一个单据的物理文件。
性能差异:kafka快很多,原因有两点,一是因为producer端将多个小消息合并,批量发向Broker。二是因为kafka是多文件并发写入,rocketMq是单文件写入。