前言
Broker的作用主要负责消息的存储、投递和查询以及服务高可用保证。Broker是RocketMQ中最重要也是最复杂的组件,本篇文章是Broker源码分析的第一篇,通过本篇文章,可以对Broker有一个初步、整体的认识。文章中的代码几乎逐行加了注释,尽可能让把相关的类都介绍了一遍,方便后续深入学习Broker其他知识。
Broker简介
简介内容截取自官网
Broker架构介绍
在 Master-Slave 架构中,Broker 分为 Master 与 Slave。一个Master可以对应多个Slave,但是一个Slave只能对应一个Master。Master 与 Slave 的对应关系通过指定相同的BrokerName,不同的BrokerId 来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。
Broker部署模型
- 每个 Broker 与 NameServer 集群中的所有节点建立长连接,定时注册 Topic 信息到所有 NameServer。
- Producer 与 NameServer 集群中的其中一个节点建立长连接,定期从 NameServer 获取Topic路由信息,并向提供 Topic 服务的 Master 建立长连接,且定时向 Master 发送心跳。Producer 完全无状态。
- Consumer 与 NameServer 集群中的其中一个节点建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Master、Slave 建立长连接,且定时向 Master、Slave发送心跳。Consumer 既可以从 Master 订阅消息,也可以从Slave订阅消息。
Broker启动过程源码分析
Broker启动的入口方法在org.apache.rocketmq.broker.BrokerStartup#main,如下所示,它与namesrv的启动过程有一些相似,主要是下面两个步骤
-
读取配置,并创建
BrokerController -
启动
BrokerController
public static void main(String[] args) {
// 读取配置文件,创建BrokerController并启动
start(createBrokerController(args));
}
创建BrokerController过程
创建BrokerController的方法在BrokerStartup#createBrokerController,这个方法创建BrokerController的核心逻辑如下
- 创建配置类BrokerConfig、NettyServerConfig、NettyClientConfig和MessageStoreConfig
- 解析命令行中
-c对应的broker.conf配置,并将配置设置到上面的配置类中 - 校验环境变量中的
ROCKETMQ_HOME是否存在,校验namesrvAddr是否正确 - 设置haListenPort端口(remotingServer监听端口(10911)+1),它用于Broker主从同步
- 通过上面4个配置创建BrokerController
- 调用
BrokerController#initialize初始化 - 注册进程关闭的钩子函数
public static BrokerController createBrokerController(String[] args) {
try {
// 如果启动命令不是mqbroker,则直接退出
Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine("mqbroker", args, buildCommandlineOptions(options),
new PosixParser());
if (null == commandLine) {
System.exit(-1);
}
// 创建broker的配置类,例如brokerClusterName,brokerName默认是本机hostname、brokerId
final BrokerConfig brokerConfig = new BrokerConfig();
// NettyServer配置类,broker作为服务端的配置类
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
// NettyClient配置类,broker作为客户端的配置类
final NettyClientConfig nettyClientConfig = new NettyClientConfig();
// tls相关配置类
nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TLS_ENABLE,
String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING))));
// nettyServerConfig监听端口
nettyServerConfig.setListenPort(10911);
// 消息存储配置
final MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
// 如果是Master节点,则消息占用内存百分比为40(默认)
// 如果是Slave节点,则消息占用内存百分比为30
if (BrokerRole.SLAVE == messageStoreConfig.getBrokerRole()) {
int ratio = messageStoreConfig.getAccessMessageInMemoryMaxRatio() - 10;
messageStoreConfig.setAccessMessageInMemoryMaxRatio(ratio);
}
// 读取命令行'-c' 参数中的配置文件,并且设置到brokerConfig,nettyServerConfig,nettyClientConfig,messageStoreConfig
if (commandLine.hasOption('c')) {
String file = commandLine.getOptionValue('c');
if (file != null) {
configFile = file;
InputStream in = new BufferedInputStream(new FileInputStream(file));
properties = new Properties();
// ... 读取配置
}
}
// 校验环境变量中是否设置ROCKETMQ_HOME
if (null == brokerConfig.getRocketmqHome()) {
System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation", MixAll.ROCKETMQ_HOME_ENV);
System.exit(-2);
}
// 校验nameserAddr地址
String namesrvAddr = brokerConfig.getNamesrvAddr();
if (null != namesrvAddr) {
try {
String[] addrArray = namesrvAddr.split(";");
for (String addr : addrArray) {
RemotingUtil.string2SocketAddress(addr);
}
} catch (Exception e) {
System.out.printf(
"The Name Server Address[%s] illegal, please set it as follows, \"127.0.0.1:9876;192.168.0.1:9876\"%n",
namesrvAddr);
System.exit(-3);
}
}
// 根据BrokerRole设置BrokerId,默认是ASYNC_MASTER
// 如果是ASYNC_MASTER,则不处理
// 如果是SYNC_MASTER,将MASTER_ID设置为0
// 如果是SLAVE,
switch (messageStoreConfig.getBrokerRole()) {
case ASYNC_MASTER:
case SYNC_MASTER:
brokerConfig.setBrokerId(MixAll.MASTER_ID);
break;
case SLAVE:
if (brokerConfig.getBrokerId() <= 0) {
System.out.printf("Slave's brokerId must be > 0");
System.exit(-3);
}
break;
default:
break;
}
// ...
// 消息存储监听端口 nettyServerPort+1
messageStoreConfig.setHaListenPort(nettyServerConfig.getListenPort() + 1);
// ...
configurator.doConfigure(brokerConfig.getRocketmqHome() + "/conf/logback_broker.xml");
// 启动命令中包含'-p'参数,打印所有配置参数,并退出
if (commandLine.hasOption('p')) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
MixAll.printObjectProperties(console, brokerConfig);
// ...
System.exit(0);
// 启动命令中包含'-m'参数,打印重要参数,并退出
} else if (commandLine.hasOption('m')) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
MixAll.printObjectProperties(console, brokerConfig, true);
// ...
System.exit(0);
}
// ...
// 创建broker
final BrokerController controller = new BrokerController(
brokerConfig,
nettyServerConfig,
nettyClientConfig,
messageStoreConfig);
// 配置记录在Controller中的Configuration中
controller.getConfiguration().registerConfig(properties);
// controller初始化
boolean initResult = controller.initialize();
// 初始化失败,则退出
if (!initResult) {
controller.shutdown();
System.exit(-3);
}
// 添加shutdownHook
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
//... 增加日志打印
}, "ShutdownHook"));
return controller;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
BrokerController构造过程
从下面源码可以看出,BrokerController在创建的过程也会创建很多配置类,线程池队列,各种Manager,Service,Listener等,BrokerController是Broker的大管家,也是Broker的中央控制器。在这里创建的每个处理器,管理器,监听器类都是相当重要的。
通过这个构造方法,可以看出BrokerController在Broker的地位是相当,相当,相当重要的!!!
BrokerController对于Broker来说有点像ApplicationContext在Spring Framework中的地位,DispatcherServlet在Spring MVC中的地位
public BrokerController(
final BrokerConfig brokerConfig,
final NettyServerConfig nettyServerConfig,
final NettyClientConfig nettyClientConfig,
final MessageStoreConfig messageStoreConfig
) {
// 设置配置
this.brokerConfig = brokerConfig;
this.nettyServerConfig = nettyServerConfig;
this.nettyClientConfig = nettyClientConfig;
this.messageStoreConfig = messageStoreConfig;
//消费者偏移量Manager,主要用于维护offset进度信息,默认是false,会创建ConsumerOffsetManager
this.consumerOffsetManager = messageStoreConfig.isEnableLmq() ? new LmqConsumerOffsetManager(this) : new ConsumerOffsetManager(this);
// topic配置Manager,默认是false,会创建TopicConfigManager
this.topicConfigManager = messageStoreConfig.isEnableLmq() ? new LmqTopicConfigManager(this) : new TopicConfigManager(this);
// 拉消息处理器,用于处理要拉去的消息
this.pullMessageProcessor = new PullMessageProcessor(this);
// 拉取消息挂起服务,如果没有要拉的消息,通过长轮询机制挂起Consumer的请求
this.pullRequestHoldService = messageStoreConfig.isEnableLmq() ? new LmqPullRequestHoldService(this) : new PullRequestHoldService(this);
// 消息到达Listener,消息到达会通知pullRequestHoldService
this.messageArrivingListener = new NotifyMessageArrivingListener(this.pullRequestHoldService);
// 消费者id变化Listener
this.consumerIdsChangeListener = new DefaultConsumerIdsChangeListener(this);
// 消费者Manager,用于维护消费者组的注册实例信息以及topic订阅信息,并且会监听consumerId的变化
this.consumerManager = new ConsumerManager(this.consumerIdsChangeListener);
// 消费者过滤器,配置在本地
this.consumerFilterManager = new ConsumerFilterManager(this);
// 生产者Manager
this.producerManager = new ProducerManager();
// 客户端心跳保持服务,用于定时扫描生产者和消费者客户端,并且将移除不活跃的客户端通道及相关信息
this.clientHousekeepingService = new ClientHousekeepingService(this);
// 处理某些broker到客户端请求,例如重置offset等
this.broker2Client = new Broker2Client(this);
// 订阅分组关系Manager,默认是false,创建SubscriptionGroupManager
this.subscriptionGroupManager = messageStoreConfig.isEnableLmq() ? new LmqSubscriptionGroupManager(this) : new SubscriptionGroupManager(this);
// broker对外发请求API,例如注销broker,注册broker,向master,slave发起请求
this.brokerOuterAPI = new BrokerOuterAPI(nettyClientConfig);
// 过滤服务管理器
this.filterServerManager = new FilterServerManager(this);
// 从节点同步器,定时向主节点发起同步请求,例如topic配置,消费位移
this.slaveSynchronize = new SlaveSynchronize(this);
// 处理来自生产者发送消息请求队列,默认10000
this.sendThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getSendThreadPoolQueueCapacity());
// 生产者推送消息处理完成之后回调线程池队列,默认10000
this.putThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getPutThreadPoolQueueCapacity());
// 消费者拉取消息请求队列,默认100000
this.pullThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getPullThreadPoolQueueCapacity());
// 处理reply消息的请求的队列,RocketMQ4.7.0版本中增加了request-reply新特性,该特性允许producer在发送消息后同步或者异步等待consumer消费完消息并返回响应消息,类似rpc调用效果。
// 即生产者发送了消息之后,可以同步或者异步的收到消费了这条消息的消费者的响应,默认10000
this.replyThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getReplyThreadPoolQueueCapacity());
// 处理查询请求队列,默认20000
this.queryThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getQueryThreadPoolQueueCapacity());
// 客户端管理器队列,默认1000000,用于处理客户端注销请求等
this.clientManagerThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getClientManagerThreadPoolQueueCapacity());
// 消费者管理器队列,默认1000000,目前好像没用到o(╥﹏╥)o
this.consumerManagerThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getConsumerManagerThreadPoolQueueCapacity());
// 心跳处理队列,默认50000,用于处理客户端的心跳
this.heartbeatThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getHeartbeatThreadPoolQueueCapacity());
// 事务消息相关处理的队列,默认100000
this.endTransactionThreadPoolQueue = new LinkedBlockingQueue<>(this.brokerConfig.getEndTransactionPoolQueueCapacity());
// broker状态管理器,用于保存broker的状态
this.brokerStatsManager = messageStoreConfig.isEnableLmq() ? new LmqBrokerStatsManager(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.isEnableDetailStat()) : new BrokerStatsManager(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.isEnableDetailStat());
// 存储的ip信息,目前好像没用到
this.setStoreHost(new InetSocketAddress(this.getBrokerConfig().getBrokerIP1(), this.getNettyServerConfig().getListenPort()));
// broker快速失败
this.brokerFastFailure = new BrokerFastFailure(this);
// 所有配置类
this.configuration = new Configuration(
log,
BrokerPathConfigHelper.getBrokerConfigPath(),
this.brokerConfig, this.nettyServerConfig, this.nettyClientConfig, this.messageStoreConfig
);
}
BrokerController初始化过程
BrokerController创建完成之后,就进行BrokerController初始化,初始化完成之后,BrokerController就算创建完成了,初始化过程可以总结为如下步骤
- 加载本地配置文件
- Topic配置文件(
TopicConfigManager) - 消费者消费偏移量配置文件(
ConsumerOffsetManager) - 订阅分组配置文件(
SubscriptionGroupManager) - 消费者过滤配置文件(
ConsumerFilterManager)
- Topic配置文件(
- 加载消息存储类(
DefaultMessageStore) - 创建Broker服务端通信类
NettyRemotingServer - 注册处理器到
NettyRemotingServer - 创建了很多定时任务线程池
- 每隔24小时打印前一天生产和消费数量
- 每隔5s将消费者offset持久化
- 每隔10s将消费过滤信息持久化
- 每隔3min,检查消费者消费进度
- 每隔1s打印发送线程池队列信息
- 如果开启fetchNamesrvAddrByAddressServer
- 每隔2min 从配置地址,获取namesrvAddr
- 如果没有开启DLeger,并且当前节点是broker节点,则每隔1min打印主从节点差异
- 创建文件监听器
- 初始化事务相关服务
- 初始化权限相关服务
- 初始化RPC调用钩子函数
public boolean initialize() throws CloneNotSupportedException {
// Topic配置文件加载,加载路径:${user.home}/store/config/topics.json
boolean result = this.topicConfigManager.load();
// 消费者消费偏移量配置文件加载,加载路径:${user.home}/store/config/consumerOffset.json
result = result && this.consumerOffsetManager.load();
// 订阅分组配置文件加载,加载路径:${user.home}/store/config/subscriptionGroup.json
result = result && this.subscriptionGroupManager.load();
// 消费者过滤配置文件加载,加载路径:${user.home}/store/config/consumerFilter.json
result = result && this.consumerFilterManager.load();
if (result) {
try {
// 消息存储类
this.messageStore =
new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener,
this.brokerConfig);
// enableDLegerCommitLog配置是是否允许自动主从切换,默认是false,主从配置是从4.5版本之后的新功能
// 如果开启了允许主从切换,则会创建DLedgerRoleChangeHandler处理器类
if (messageStoreConfig.isEnableDLegerCommitLog()) {
DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore);
((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler);
}
this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore);
// 加载消息存储插件
MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig);
this.messageStore = MessageStoreFactory.build(context, this.messageStore);
// 添加消息过滤器
this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));
} catch (IOException e) {
result = false;
log.error("Failed to initialize", e);
}
}
// 消息存储类加载,默认是DefaultMessageStore
result = result && this.messageStore.load();
if (result) {
// 远程通信服务类
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);
// 远程服务配置监听端口类 默认端口是服务监听端口-2,10909
NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone();
fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2);
this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService);
// 处理发送消息请求线程池,默认线程数量:min(处理器数量,4)
this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor(/*...*/);
// 生产者推送消息处理完成线程池,默认线程数量:min(处理器数量,4)
this.putMessageFutureExecutor = new BrokerFixedThreadPoolExecutor(/*...*/);
// 消费者拉取消息线程池,默认线程数量:16+处理器数量*2
this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor(/*...*/);
// 消费者消费消息响应线程池,默认线程数量:16+处理器数量*2
this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor(/*...*/);
// 查询消息线程池:默认线程池数量:8+处理器数量
this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor(/*...*/);
// broker管理线程池,默认线程数量:16
this.adminBrokerExecutor =
Executors.newFixedThreadPool(/*...*/);
// 客户端管理线程池
this.clientManageExecutor = new ThreadPoolExecutor(/*...*/);
// 心跳处理线程池
this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor(/*...*/);
// 事务处理线程池
this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor(/*...*/);
// 消费者管理线程池
this.consumerManageExecutor =
Executors.newFixedThreadPool(/*...*/);
// 请求处理器注册,注册到RemotingServer
this.registerProcessor();
final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis();
final long period = 1000 * 60 * 60 * 24;
// 每隔24小时打印前一天生产和消费数量
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
// 每隔5s将消费者offset持久化
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
// 每隔10s将消费过滤信息持久化
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
// 每隔3min,检查消费者消费进度,开启disableConsumeIfConsumerReadSlowly时生效,默认是false
// 当消费进度落后阈值时,就会停止消费者组,保护broker
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
// 每隔1s打印发送消息线程池队列、拉取消息线程池队列、查询消息线程池队列、结束事务线程池队列的大小,队列头部元素存活时间
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
// 每隔1min打印在commitLog中提交,但是还没分配到consumerQueue的字节数
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
if (this.brokerConfig.getNamesrvAddr() != null) {
// 如果brokerConfig中namesrvAddr不为空,则每隔2min更新remotingClient中的namesrvAddr
this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
// 如果打开fetchNamesrvAddrByAddressServer配置
// 每隔2min 从配置地址,获取namesrvAddr
// 地址如下:http://${rocketmq.namesrv.domain}/rocketmq/${rocketmq.namesrv.domain.subgroup}
} else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
this.scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
BrokerController.this.brokerOuterAPI.fetchNameServerAddr();
} catch (Throwable e) {
log.error("ScheduledTask fetchNameServerAddr exception", e);
}
}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
}
// 如果没有开启高可用服务,配置enableDLegerCommitLog,默认就是没有开启
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
// 如果当前broker是从节点
if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
// 根据是否配置了HA地址,来更新HA地址
if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {
this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());
this.updateMasterHAServerAddrPeriodically = false;
} else {
this.updateMasterHAServerAddrPeriodically = true;
}
// 如果当前节点是主节点
} else {
// 每隔60s打印主从节点差异
this.scheduledExecutorService.scheduleAtFixedRate(/*...*/);
}
}
// TSL文件传输配置,是通信安全的文件监听模块
// 默认配置是PERMISSIVE,因此会进入代码块
if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
// ... 省略文件监听代码
}
// 初始化事务相关服务
initialTransaction();
// 初始化权限相关服务
initialAcl();
// 初始化RPC调用钩子函数
initialRpcHooks();
}
return result;
}
BrokerController启动过程
BrokerController启动过程调用了BrokerController#start,代码如下删除了部分不影响源码分析的代码,整个启动过程主要包括下面两个方面
- 启动本地服务类
- 给namesrv发送心跳
public void start() throws Exception {
// 消息存储类实现类
this.messageStore.start();
// netty网络服务端启动
this.remotingServer.start();
// netty网络服务端启动
this.fastRemotingServer.start();
// 文件MD5校验
this.fileWatchService.start();
// 网络通信客户端启动
this.brokerOuterAPI.start();
// 长轮询处理服务
this.pullRequestHoldService.start();
// 客户端活跃连接扫描服务
this.clientHousekeepingService.start();
// 创建FilterServer
this.filterServerManager.start();
// 默认是关闭,会走里面的逻辑
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
// 如果是主节点,则开启事务消息校验/60s
startProcessorByHa(messageStoreConfig.getBrokerRole());
// 如果是从节点,则开启数据同步/10s
handleSlaveSynchronize(messageStoreConfig.getBrokerRole());
// 给namesrv发送心跳
this.registerBrokerAll(true, false, true);
}
// 每隔30s发送一次心跳给namesrv
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
// ...
BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
// ...
}, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod()/*30s*/, 60000)), TimeUnit.MILLISECONDS);
// broker状态管理
this.brokerStatsManager.start(); // doing nothing
// 快速失败服务启动
this.brokerFastFailure.start(); // BrokerStatsManager
}
总结
Broker启动都是围绕BrokerController展开的,BrokerController扮演着举足轻重的地位,Broker整个启动过程可以分为下面三个阶段,通过学习Broker启动过程源码,对于Broker一些关键组件更加熟悉。