环境搭建
源码拉取
从官方仓库 https://github.com/apache/rocketmq clone或者download源码。

源码目录结构:
-
broker: broker 模块(broke 启动进程)
-
client :消息客户端,包含消息生产者、消息消费者相关类
-
common :公共包
-
dev :开发者信息(非源代码)
-
distribution :部署实例文件夹(非源代码)
-
example: RocketMQ 例代码
-
filter :消息过滤相关基础类
-
filtersrv:消息过滤服务器实现相关类(Filter启动进程)
-
logappender:日志实现相关类
-
namesrv:NameServer实现相关类(NameServer启动进程)
-
openmessageing:消息开放标准
-
remoting:远程通信模块,给予Netty
-
srcutil:服务工具类
-
store:消息存储实现相关类
-
style:checkstyle相关实现
-
test:测试相关类
-
tools:工具类,监控命令相关实现类
执行安装
clean install -Dmaven.test.skip=true
调试
创建conf配置文件夹,从distribution拷贝broker.conf和logback_broker.xml和logback_namesrv.xml

1)启动NameServer
- 展开namesrv模块,右键NamesrvStartup.java

- 配置ROCKETMQ_HOME


- 重新启动
控制台打印结果
The Name Server boot success. serializeType=JSON
2)启动Broker
broker.conf配置文件内容
brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
# namesrvAddr地址
namesrvAddr=127.0.0.1:9876
deleteWhen = 04
#保留时长
fileReservedTime = 48
brokerRole = ASYNC_MASTER
#刷盘机制
flushDiskType = ASYNC_FLUSH
# 自动创建路由
autoCreateTopicEnable=true
# 存储路径
storePathRootDir=E:\\RocketMQ\\data\\rocketmq\\dataDir
# commitLog路径
storePathCommitLog=E:\\RocketMQ\\data\\rocketmq\\dataDir\\commitlog
# 消息队列存储路径
storePathConsumeQueue=E:\\RocketMQ\\data\\rocketmq\\dataDir\\consumequeue
# 消息索引存储路径
storePathIndex=E:\\RocketMQ\\data\\rocketmq\\dataDir\\index
# checkpoint文件路径
storeCheckpoint=E:\\RocketMQ\\data\\rocketmq\\dataDir\\checkpoint
# abort文件存储路径
abortFile=E:\\RocketMQ\\data\\rocketmq\\dataDir\\abort
- 创建数据文件夹
dataDir - 启动
BrokerStartup,配置broker.conf和ROCKETMQ_HOME


3)发送消息
- 进入example模块的
org.apache.rocketmq.example.quickstart - 指定Namesrv地址
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
producer.setNamesrvAddr("127.0.0.1:9876");
- 运行
main方法,发送消息
4)消费消息
- 进入example模块的
org.apache.rocketmq.example.quickstart - 指定Namesrv地址
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
consumer.setNamesrvAddr("127.0.0.1:9876");
- 运行
main方法,消费消息
NameServer
架构设计
消息中间件的设计思路一般是基于主题订阅发布的机制,消息生产者(Producer)发送某一个主题到消息服务器,消息服务器负责将消息持久化存储,消息消费者(Consumer)订阅该兴趣的主题,消息服务器根据订阅信息(路由信息)将消息推送到消费者(Push模式)或者消费者主动向消息服务器拉去(Pull模式),从而实现消息生产者与消息消费者解耦。为了避免消息服务器的单点故障导致的整个系统瘫痪,通常会部署多台消息服务器共同承担消息的存储。那消息生产者如何知道消息要发送到哪台消息服务器呢?如果某一台消息服务器宕机了,那么消息生产者如何在不重启服务情况下感知呢?
NameServer就是为了解决以上问题设计的。
(监听Broker心跳,broker服务注册)

Broker消息服务器在启动的时向所有NameServer注册,消息生产者(Producer)在发送消息时之前先从NameServer获取Broker服务器地址列表,然后根据负载均衡算法从列表中选择一台服务器进行发送。NameServer与每台Broker保持长连接,并间隔30S检测Broker是否存活,如果检测到Broker宕机,则从路由注册表中删除。但是路由变化不会马上通知消息生产者。这样设计的目的是为了降低NameServer实现的复杂度,在消息发送端提供容错机制保证消息发送的可用性。
NameServer本身的高可用是通过部署多台NameServer来实现,但彼此之间不通讯,也就是NameServer服务器之间在某一个时刻的数据并不完全相同,但这对消息发送并不会造成任何影响,这也是NameServer设计的一个亮点,总之,RocketMQ设计追求简单高效。
启动流程

启动类:org.apache.rocketmq.namesrv.NamesrvStartup
步骤一
解析配置文件,填充NameServerConfig、NettyServerConfig属性值,并创建NamesrvController
(创建NameSrvController)
步骤二
根据启动属性创建NamesrvController实例,并初始化该实例。NameServerController实例为NameServer核心控制器
(初始化NameSrvController,开启定时任务监听broker心跳)
broker长时间没有心跳会被剔除
================================================================================
RocketMQ介绍
RocketMQ 是阿里巴巴集团基于高可用分布式集群技术,自主研发的云正式商用的专业消息中间件,既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性,是阿里巴巴双 11 使用的核心产品。
RocketMQ源码中的技术亮点
- 读写锁
- 原子操作类
- 文件存储设计
- 零拷贝:MMAP
- 线程池
- ConcurrentHashMap
- 写时复制容器
- 负载均衡策略
- 故障延迟机制
- 堆外内存
RocketMQ核心组件

NameServer
命名服务,更新和路由发现 broker服务。NameServer的作用是为消息生产者、消息消费者提供关于主题 Topic 的路由信息,NameServer除了要存储路由的基础信息,还要能够管理 Broker节点,包括路由注册、路由删除等功能。
Producer/Consumer
java版本的MQ客户端实现,包括生产者和消费者。
Broker
它能接收producer和consumer的请求,并调用store层服务对消息进行处理。HA服务的基本单元,支持同步双写,异步双写等模式。
Store
存储层实现,同时包括了索引服务,高可用HA服务实现。
Netty Remoting Server/Netty Remoting Client
基于netty的底层通信实现,所有服务间的交互都基于此模块。也区分服务端和客户端
RocketMQ的核心三流程
启动流程
RocketMQ服务端由两部分组成NameServer和Broker,NameServer是服务的注册中心,Broker会把自己的地址注册到NameServer,生产者和消费者启动的时候会先从NameServer获取Broker的地址,再去从Broker发送和接受消息。
消息生产流程
Producer将消息写入到RocketMQ集群中Broker中具体的Queue。
消息消费流程
Consumer从RocketMQ集群中拉取对应的消息并进行消费确认。
启动流程
RocketMQ服务端由两部分组成NameServer和Broker,NameServer是服务的注册中心,Broker会把自己的地址注册到NameServer,生产者和消费者启动的时候会先从NameServer获取Broker的地址,再去从Broker发送和接受消息。

-
NameServer启动
启动监听,等待Broker、Producer、Comsumer连接。
Broker在启动时向所有NameServer注册,生产者在发送消息之前先从NameServer获取Broker服务器地址列表,然后根据负载均衡算法从列表中选择一台服务器进行消息发送。消费者在订阅某个主题的消息之前从NamerServer获取Broker服务器地址列表(有可能是集群),但是消费者选择从Broker中订阅消息,订阅规则由 Broker 配置决定。
(将Broker注册到namesrv) -
路由注册
Broker启动后向所有NameServer发送路由及心跳信息。 -
路由剔除
移除心跳超时的Broker相关路由信息。NameServer与每台Broker服务保持长连接,并间隔10S检查Broker是否存活(使用定时线程池ScheduledExecutorService检测),如果检测到Broker宕机,则从路由注册表中将其移除。这样就可以实现RocketMQ的高可用。
(Broker每30s发送一次心跳给nameSrv,nameSrv每10s扫描一次,120s没心跳 路由不可用 清楚路由;如果是正常关闭直接移除路由信息)
NameServer整体流程
NameServer是整个RocketMQ的“大脑”,它是RocketMQ的服务注册中心,所以RocketMQ需要先启动NameServer再启动Rocket中的Broker。
(核心就是进行路由注册;同时开启定时任务,剔除过期路由)

NameServer无状态化

- NameServer集群中它们相互之间是不通讯
- 主从架构中,Broker都会向所有NameServer注册路由、心跳信息
- 生产者/消费者同一时间,与NameServer集群中其中一台建立长连接
项目实战部署分析:
假设一个RocketMQ集群部署在两个机房,每个机房都有一些NameServer、Broker和客户端节点,当两个机房的链路中断时,所有的NameServer都可以提供服务,客户端只能在本机房的NameServer中找到本机房的Broker。
RocetMQ集群中,NameSever之间是不需要互相通信的,所以网络分区对NameSever本身的可用性是没有影响的,如果NameSever检测到与Broker的连接中断了,NameServer会认为这个Broker不再能提供服务,NameServer会立即把这个Broker从路由信息中移除掉,避免客户端连接到一个不可用的Broker上去。
网络分区后,NameSever 收不到对端机房那些Broker的心跳,这时候,每个Namesever上都只有本机房的Broker信息。
NameServer设计亮点
(读写锁,存储基于内存)
读写锁
RouteInfoManager类中有一个读写锁的设计

消息发送时客户端会从NameServer获取路由信息,同时Broker会定时更新NameServer的路由信息,所以路由表会有非常频繁的以下操作:
1、 生产者发送消息时需要频繁的获取。对表进行读。
RouteInfoManager类

2、 Broker定时(30s)会更新一个路由表。对表进行写。
RouteInfoManager类

因为Broker每隔30s向NameServer发送一个心跳包,这个操作每次都会更新Broker的状态,但同时生产者发送消息时也需要Broker的状态,要进行频繁的读取操作。所以这个地方就有一个矛盾,Broker的状态会被经常性的更新,同时也会被更加频繁的读取。这里如何提高并发,尤其是生产者进行消息发送时的并发,所以这里使用了读写锁机制(针对读多写少的场景)。
synchronized和ReentrantLock基本都是排他锁,排他锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
存储基于内存
NameServer存储以下信息:
topicQueueTable:Topic消息队列路由信息,消息发送时根据路由表进行负载均衡
brokerAddrTable:Broker基础信息,包括brokerName、所属集群名称、主备Broker地址
clusterAddrTable:Broker集群信息,存储集群中所有Broker名称
brokerLiveTable:Broker状态信息,NameServer每次收到心跳包是会替换该信息
filterServerTable:Broker上的FilterServer列表,用于类模式消息过滤。

NameServer的实现基于内存,NameServer并不会持久化路由信息,持久化的重任是交给Broker来完成。这样设计可以提高NameServer的处理能力。
Broker源码分析 消息存储
Broker 启动流程


(每30s向namesrv发送心跳包)
亮点
写时复制容器
消息存储设计

RocketMQ 主要存储的文件包括 Commitlog 文件、 ConsumeQueue 文件、 IndexFile。RocketMQ 将所有主题的消息存储在同一文件,确保消息发送时顺序写文件,尽最大的能力确保消息发送的高性能与高吞吐量。但由于一般的消息中间件是基于消息主题的订阅机制,这样便给按照消息主题检索消息带来了极大的不便。为了提高消息消费的效率, RocketMQ 引入了 ConsumeQueue 消息队列文件,每个消息主题包含多个消息消费队列,每个消息队列有一个消息文件。RocketMQ 还引入了IndexFile 索引文件,其主要设计理念就是为了加速消息的检索性能,可以根据消息的属性快速从 Commitlog 文件中检索消息。整体如下:

1 ) CommitLog :消息存储文件,所有消息主题的消息都存储在 CommitLog 文件中2 ) ConsumeQueue :消息消费队列,消息到达 CommitLog 文件后,将异步转发到消息消费队列,供消息消费者消费3 ) IndexFile :消息索引文件,主要存储消息 Key与Offset 的对应关系
零拷贝技术之MMAP提升文件读写性能

同步双写


3409

被折叠的 条评论
为什么被折叠?



