1.NameServer名称服务
NameServer是没有状态的,即NameServer中的Broker和topic等状态信息(通过其他角色上报获取)都是保存在内存中的,不会持久化存储(可通过配置实现),集群可以横向扩展。主要功能如下:
a.接收Broker(master和slave)启动时的注册路由信息;
b.为producer和consumer提供路由服务,即通过topic名字获取所有broker的路由信息;
c.接收broker发送的心跳信息,如果心跳的时间戳过期NameServer关闭与broker的连接。
2.Broker
Broker向NameServer注册topic配置信息,配置信息格式如下:
{
"perm":6,
"readQueueNums":2,
"topicFilterType":"SINGLE_TAG",
"topicName":"Topic-Lance",
"writeQueueNums":5
}
3:Broker的消息存储
Rocketmq的消息的存储是由consumeQueue和 commitLog 配合完成的,commitLog保存消息的物理数据,consumeQueue是消息的逻辑队列,类似于索引,存储的是指向物理存储的地址。在一个Broker上,只有一个commitLog,所有consumeQueue共享同一个commitLog。
假如topic的名字是Topic-Lance,配置的读写队列有queue-1和queue-2,那么Topic-Lance和queue-1组成一个consumeQueue,Topic-Lance和queue-2组成另一个consumeQueue。
假如broker-A(包含queue-0,queue-1,queue-2), broker-B(包含queue-0,queue-1)两台broker机器都配置了Topic-Lance,那么broker启动的时候,注册到NameServer的Topic-Lance的路由有broker-A-queue-0,broker-A-queue-1,broker-A-queue-2,broker-B-queue-0,broker-B-queue-1共5个consumeQueue。
为了提高读写性能,commitLog采取顺序写,随机读(通过pagecache机制批量从磁盘读取到内存,加速后续的读取速度),consumeQueue大部分读入内存(如果consumeQueue因为重启等因素丢失,可以通过commitLog重建)
Page cache是通过将磁盘中的数据缓存到内存中,从而减少磁盘I/O操作,从而提高性能。此外,还要确保在page cache中的数据更改时能够被同步到磁盘上,后者被称为page回写(page writeback)。一个inode对应一个page cache对象,一个page cache对象包含多个物理page。
对磁盘的数据进行缓存从而提高性能主要是基于两个因素:第一,磁盘访问的速度比内存慢好几个数量级(毫秒和纳秒的差距)。第二是被访问过的数据,有很大概率会被再次访问。
4.Producer 生产者
a.Producer发送消息时(必须制定topic),首先从本地的Producer集合中获取topic->broker的路由信息,如果没有,则从 nameserver中获取topic->broker路由,并缓存到本地集合;
b.定时从nameServer获取最新的topic路由信息;
c.Producer定时将Producer的group信息发送到对应的broker上;
d.Producer发送消息到Master的broker上,通过Broker的主从复制copy到slave的broker上。
5.Consumer 消费者
a.向NameServer注册Consumer;
b.定时从NameServer获取topic路由信息;
c.定时清理下线的broker;
d.向所有broker发送心跳;
e.动态调整消费线程池;
f.负责负载均衡服务RebalanceService。
RocketMQ是基于pull模式拉取消息,consumer做负载均衡并通过长轮询向broker拉消息,长轮询拉取消息后回调MessageListener接口实现完成消费。
关于RocketMQ长轮询可参考:https://www.jianshu.com/p/48dbc9eee890
6.数据丢失问题处理
RocketMQ默认保存3天,commit log刷盘间隔,默认1秒
7.消费重试机制
Producer端重试 :
默认情况下是失败3次重试,可通过retryTimesWhenSendFailed定义重试次数;
Consumer端重试:
1.Exception的情况,一般重复16次 10s、30s、1mins、2mins、3mins等,可以通过设置transactionCheckMax设置;
2.超时情况(Consumer端没有返回CONSUME_SUCCESS,也没有返回RECONSUME_LATER),MQ会无限制的发送给Consumer端,默认超时时间时15分钟。
8. Rocketmq消费模型(实时性)
常见的数据同步方式有这几种:
push:producer发送消息后,broker马上把消息投递给consumer。这种方式好在实时性比较高,但是会增加broker的负载;而且消费端能力不同,如果push推送过快,消费端会出现很多问题。
pull:producer发送消息后,broker什么也不做,等着consumer自己来读取。它的优点在于主动权在消费者端,可控性好;但是间隔时间不好设置,间隔太短浪费资源,间隔太长又会消费不及时。
长轮询:当consumer过来请求时,broker会保持当前连接一段时间 默认15s,如果这段时间内有消息到达,则立刻返回给consumer;15s没消息的话则返回空然后重新请求。这种方式的缺点就是服务端要保存consumer状态,客户端过多会一直占用资源。
RocketMQ默认是采用pushConsumer方式消费的,从概念上来说是推送给消费者,它的本质是pull+长轮询。这样既通过长轮询达到了push的实时性,又有了pull的可控性。系统收到消息后会自动处理消息和offset(消息偏移量),如果期间有新的consumer加入会自动做负载均衡(集群模式下offset存在broker中; 广播模式下offset存在consumer里)。当然我们也可以设置为pullConsumer模式,这样灵活性会提高,但是代码却会很复杂,需要手动维护offset,消息存储和状态。
* offset:简单粗暴的理解就是数组下标。message queue是无限长的数组,每次消息进来就会涨1,下标就是offset。consumer可以通过指定offse位置开始读取数据。queue的maxOffset是消息的最大offset,不是最新消息的offset 而是最新消息的offset+1,minOffset则是现存的最小offset。
9. Rocketmq消息存储(顺序写,随机读)
消息存储是由ConsumeQueue和CommitLog配合完成的。一个Topic里面有多个MessageQueue,每个MessageQueue对应一个ConsumeQueue.
默认地址:store/consumequeue/{topicName}/{queueid}/fileName
ConsumeQueue里记录着消息物理存储地址。(读:consumer根据消息的consumeQueue找到消息存储具体路径,从而读取里面信息)
CommitLog就存储文件具体的字节信息。(写:文件大小默认1g,文件名称20位数 左边补0右边为偏移量。消息顺序写入文件,文件满了则写入下一个文件)
10. ZeroCopy高性能零拷贝
linux有两个上下文(内核态、用户态), 传统的将一个file读取并发送出去会经历4个过程。
read时:
1. 将文件从磁盘copy到kernel(内核)态
2. cpu将kernrl态的数据copy到user(用户)态
write时:
3. user态的内容会copy到kernel态的socket的buffer中
4. 将kernel中buffer的数据copy到网卡中传送
我们可以发现2、3完全是多余的步骤,而且上下文之间的切换是很耗性能的。
ZeroCopy:内核直接把磁盘的数据传输到socket,而不是通过应用程序去传输。减少了不必要的内核缓冲区和用户缓冲区间的拷贝,从而提升了性能。
零拷贝技术有mmap及sendfile;sendfile大文件传输快,mmap小文件传输快。MMQ发送的消息通常都很小,rocketmq就是以mmap+write方式实现的。像kafka、netty都采用了零拷贝技术。