Broker的启动也和NameSrv一样,主要有createBrokerController和start这2个方法
一、createBrokerController方法源码
public static BrokerController createBrokerController(String[] args) {
System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));
if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_SNDBUF_SIZE)) {
NettySystemConfig.socketSndbufSize = 131072;
}
if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_RCVBUF_SIZE)) {
NettySystemConfig.socketRcvbufSize = 131072;
}
try {
Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine("mqbroker", args, buildCommandlineOptions(options),
new PosixParser());
if (null == commandLine) {
System.exit(-1);
}
final BrokerConfig brokerConfig = new BrokerConfig();
final NettyServerConfig nettyServerConfig = new NettyServerConfig();
final NettyClientConfig nettyClientConfig = new NettyClientConfig();
nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TLS_ENABLE,
String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING))));
// 设置Netty服务端端口号是10911
nettyServerConfig.setListenPort(10911);
// 获取包括commitlog文件的存储路径、commitlog文件大小在内的基本信息
final MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
// 如果当前Broker是从机,将accessMessageInMemoryMaxRatio-10
if (BrokerRole.SLAVE == messageStoreConfig.getBrokerRole()) {
int ratio = messageStoreConfig.getAccessMessageInMemoryMaxRatio() - 10;
messageStoreConfig.setAccessMessageInMemoryMaxRatio(ratio);
}
// 读取-c对应的配置文件,并赋值到对象中
if (commandLine.hasOption('c')) {
String file = commandLine.getOptionValue('c');
if (file != null) {
configFile = file;
InputStream in = new BufferedInputStream(new FileInputStream(file));
properties = new Properties();
properties.load(in);
properties2SystemEnv(properties);
MixAll.properties2Object(properties, brokerConfig);
MixAll.properties2Object(properties, nettyServerConfig);
MixAll.properties2Object(properties, nettyClientConfig);
MixAll.properties2Object(properties, messageStoreConfig);
BrokerPathConfigHelper.setBrokerConfigPath(file);
in.close();
}
}
MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), brokerConfig);
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);
}
// 通过try catch来校验namesrvAddr是否满足规则
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);
}
}
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;
}
if (messageStoreConfig.isEnableDLegerCommitLog()) {
brokerConfig.setBrokerId(-1);
}
// 设置HA监听端口,暂不知道作用
messageStoreConfig.setHaListenPort(nettyServerConfig.getListenPort() + 1);
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
lc.reset();
configurator.doConfigure(brokerConfig.getRocketmqHome() + "/conf/logback_broker.xml");
if (commandLine.hasOption('p')) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
MixAll.printObjectProperties(console, brokerConfig);
MixAll.printObjectProperties(console, nettyServerConfig);
MixAll.printObjectProperties(console, nettyClientConfig);
MixAll.printObjectProperties(console, messageStoreConfig);
System.exit(0);
} else if (commandLine.hasOption('m')) {
InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
MixAll.printObjectProperties(console, brokerConfig, true);
MixAll.printObjectProperties(console, nettyServerConfig, true);
MixAll.printObjectProperties(console, nettyClientConfig, true);
MixAll.printObjectProperties(console, messageStoreConfig, true);
System.exit(0);
}
log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
MixAll.printObjectProperties(log, brokerConfig);
MixAll.printObjectProperties(log, nettyServerConfig);
MixAll.printObjectProperties(log, nettyClientConfig);
MixAll.printObjectProperties(log, messageStoreConfig);
// 创建BrokerController对象,这里还会创建Topic,默认8个
final BrokerController controller = new BrokerController(
brokerConfig,
nettyServerConfig,
nettyClientConfig,
messageStoreConfig);
// 将命令行中设置的参数添加到对象中
controller.getConfiguration().registerConfig(properties);
// 见下面
boolean initResult = controller.initialize();
if (!initResult) {
controller.shutdown();
System.exit(-3);
}
// JVM退出时,执行回调函数
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
private volatile boolean hasShutdown = false;
private AtomicInteger shutdownTimes = new AtomicInteger(0);
@Override
public void run() {
synchronized (this) {
log.info("Shutdown hook was invoked, {}", this.shutdownTimes.incrementAndGet());
if (!this.hasShutdown) {
this.hasShutdown = true;
long beginTime = System.currentTimeMillis();
controller.shutdown();
long consumingTimeTotal = System.currentTimeMillis() - beginTime;
log.info("Shutdown hook over, consuming total time(ms): {}", consumingTimeTotal);
}
}
}
}, "ShutdownHook"));
return controller;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
这里有几点要注意一下:
1)RocketMQ启动时可以通过
-c XXX.properties
命令来读取配置文件信息。properties文件如图,它主要有以下几个属性
- brokerName:同一组broker的brokerName必须相同
- brokerRole:有三种模式:ASYNC_MASTER、SYNC_MASTER、SLAVE。RocketMQ官网推荐,为保证数据不丢失,使用SYNC_MASTER和SLAVE模式,允许数据丢失,使用ASYNC_MASTER和SLAVE模式
- flushDiskType:刷盘模式,有2种:ASYNC_FLUSH和SYNC_FLUSH。为了保证数据不丢失,使用SYNC_FLUSH,否则使用ASYNC_FLUSH
- brokerId:-1:开启了dledger模式;0:SYNC_MASTER模式;>0:SLAVE模式
- enableDLegerCommitLog:是否开启dledger模式
- dLegerGroup:与brokerName相同
- dLegerSelfId:节点名,必须属于dLegerPeers中的一个
- dLegerPeers:dLegerGroup下各节点的端口信息,每个节点的配置都要一样
- sendMessageThreadPoolNums:发送线程数
brokerClusterName=MyRaftCluster
brokerName=MyRaftBroker
brokerId=0
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
enableDLegerCommitLog=true
dLegerGroup=MyRaftBroker
dLegerSelfId=n0
dLegerPeers=n0-10.5.13.76:40911
sendMessageThreadPoolNums=16
2)broker客户端默认监听端口是10911,HA端口是监听端口+1,服务端端口是监听端口-2
3)在DefaultMessageStore的构造方法中,开启dLedger模式后,haService就是null,是因为haService和dLedger和冲突了吗?
二、start方法如下
这里启动了很多线程,每个线程的作用还需进一步研究
public void start() throws Exception {
// 获取consumequeue文件中的最大offset,这个offset不等小于commitlog的最小offset。应该是在broker启动后,处理一些积压的数据,比较复杂
if (this.messageStore != null) {
this.messageStore.start();
}
// 创建Netty服务端,端口号10911
if (this.remotingServer != null) {
this.remotingServer.start();
}
// 创建Netty服务端,端口号10909
if (this.fastRemotingServer != null) {
this.fastRemotingServer.start();
}
// 创建守护线程,执行FileWatchService类中的start方法
if (this.fileWatchService != null) {
this.fileWatchService.start();
}
// 创建客户端
if (this.brokerOuterAPI != null) {
this.brokerOuterAPI.start();
}
// 创建守护线程,执行PullRequestHoldService类中的start方法
if (this.pullRequestHoldService != null) {
this.pullRequestHoldService.start();
}
// 监听生产者、消费者的心跳,将不活跃的连接关闭
if (this.clientHousekeepingService != null) {
this.clientHousekeepingService.start();
}
// 应该和过滤有关
if (this.filterServerManager != null) {
this.filterServerManager.start();
}
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
// 如果当前broker是master,启动守护线程类TransactionalMessageCheckService
startProcessorByHa(messageStoreConfig.getBrokerRole());
// 感觉像是主从同步用的
handleSlaveSynchronize(messageStoreConfig.getBrokerRole());
// 将Broker的ip、端口、Topic等信息注册到namesrv
this.registerBrokerAll(true, false, true);
}
// 每隔30秒注册,相当于心跳
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
} catch (Throwable e) {
log.error("registerBrokerAll Exception", e);
}
}
}, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);
if (this.brokerStatsManager != null) {
this.brokerStatsManager.start();
}
if (this.brokerFastFailure != null) {
this.brokerFastFailure.start();
}
}
三、注册broker
BrokerController类中的registerBrokerAll方法会往namesrv注册broker信息,在程序启动以及每30秒触发一次
public synchronized void registerBrokerAll(final boolean checkOrderConfig, boolean oneway, boolean forceRegister) {
// 获取所有Topic信息
TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper();
if (!PermName.isWriteable(this.getBrokerConfig().getBrokerPermission())
|| !PermName.isReadable(this.getBrokerConfig().getBrokerPermission())) {
ConcurrentHashMap<String, TopicConfig> topicConfigTable = new ConcurrentHashMap<String, TopicConfig>();
for (TopicConfig topicConfig : topicConfigWrapper.getTopicConfigTable().values()) {
TopicConfig tmp =
new TopicConfig(topicConfig.getTopicName(), topicConfig.getReadQueueNums(), topicConfig.getWriteQueueNums(),
this.brokerConfig.getBrokerPermission());
topicConfigTable.put(topicConfig.getTopicName(), tmp);
}
topicConfigWrapper.setTopicConfigTable(topicConfigTable);
}
if (forceRegister || needRegister(this.brokerConfig.getBrokerClusterName(),
this.getBrokerAddr(),
this.brokerConfig.getBrokerName(),
this.brokerConfig.getBrokerId(),
this.brokerConfig.getRegisterBrokerTimeoutMills())) {
doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper);
}
}