RocketMQ源碼(一):NameServer的啟動
RocketMQ源碼(三):broker的啟動(二)
RocketMQ源碼(四):producer的啟動
RocketMQ源碼(五):producer發送消息
RocketMQ源碼(六):broker接收消息
RocketMQ源碼(七):consumer的啟動
RocketMQ源碼(八):consumer消息拉取(一)
RocketMQ源碼(九):consumer消息拉取(二)
從RocketMq的整體架構來看,broker就像是一個管家。不過它管理的是消息。包括但不限于:消息接收、消息存儲、提供消息的查詢等等。
和NameServer一樣,broker也可以通過BrokerStartup的main方法來啟動,同樣的brokercontroller是broker的核心控制類。這里主要通過兩個步驟來完成啟動:
- createBrokerController
- controller.start()
先從第一步開始看broker的啟動過程
public static BrokerController createBrokerController(String[] args) {
System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));
// Socket參數,TCP數據發送緩沖區大小,默認128k
if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_SNDBUF_SIZE)) {
NettySystemConfig.socketSndbufSize = 131072;
}
// Socket參數,TCP數據接收緩沖區大小,默認128k
if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_RCVBUF_SIZE)) {
NettySystemConfig.socketRcvbufSize = 131072;
}
try {
//PackageConflictDetect.detectFastjson();
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();
// 封裝了其作為NameServer客戶端的信息
final NettyClientConfig nettyClientConfig = new NettyClientConfig();
nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TLS_ENABLE,
String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING))));
// 設置監聽端口號
nettyServerConfig.setListenPort(10911);
// MessageStoreConfig會默認配置BrokerRole為ASYNC_MASTER
final MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
// 如果是從節點,消息最大內存占比比主節點少 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);
}
// 檢查集群地址
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);
}
}
// master的brokerId為0,slave為大于0
// Broker 角色分為
// ASYNC_MASTER(異步主機),異步同步消息到slave
// SYNC_MASTER(同步主機),同步同步消息到slave
// 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;
}
if (messageStoreConfig.isEnableDLegerCommitLog()) {
brokerConfig.setBrokerId(-1);
}
// 通過-p和-m命令加載配置
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
final BrokerController controller = new BrokerController(
brokerConfig,
nettyServerConfig,
nettyClientConfig,
messageStoreConfig);
// remember all configs to prevent discard
controller.getConfiguration().registerConfig(properties);
// 初始化BrokerController
boolean initResult = controller.initialize();
if (!initResult) {
controller.shutdown();
System.exit(-3);
}
// 注冊虛擬機的shutdown鉤子函數
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;
}
出了讀取配置以外,這里有兩個地方比較重要
- BrokerController實例化
- controller.initialize(),BrokerController初始化
1.BrokerController的實例化
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;
// @1
this.consumerOffsetManager = new ConsumerOffsetManager(this);
// @2
this.topicConfigManager = new TopicConfigManager(this);
// 處理consumer的消息拉取
this.pullMessageProcessor = new PullMessageProcessor(this);
// 消息拉取請求掛起服務。當消息拉取請求拉取不到數據時,鏈接掛起一段時間
this.pullRequestHoldService = new PullRequestHoldService(this);
// 消息達到通知,用于通知pullRequestHoldService中掛起的鏈接
this.messageArrivingListener = new NotifyMessageArrivingListener(this.pullRequestHoldService);
// consumer信息變更(變更、注冊、解除注冊)監聽
this.consumerIdsChangeListener = new DefaultConsumerIdsChangeListener(this);
// @3
this.consumerManager = new ConsumerManager(this.consumerIdsChangeListener);
// consumer過濾信息管理
this.consumerFilterManager = new ConsumerFilterManager(this);
// @4
this.producerManager = new ProducerManager();
// 掃描客戶端鏈接是否超時
this.clientHousekeepingService = new ClientHousekeepingService(this);
this.broker2Client = new Broker2Client(this);
this.subscriptionGroupManager = new SubscriptionGroupManager(this);
// broker向NameSrv發送請求的客戶端
this.brokerOuterAPI = new BrokerOuterAPI(nettyClientConfig);
this.filterServerManager = new FilterServerManager(this);
// 從節點異步同步主節點數據
this.slaveSynchronize = new SlaveSynchronize(this);
this.sendThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getSendThreadPoolQueueCapacity());
this.pullThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getPullThreadPoolQueueCapacity());
this.replyThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getReplyThreadPoolQueueCapacity());
this.queryThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getQueryThreadPoolQueueCapacity());
this.clientManagerThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getClientManagerThreadPoolQueueCapacity());
this.consumerManagerThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getConsumerManagerThreadPoolQueueCapacity());
this.heartbeatThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getHeartbeatThreadPoolQueueCapacity());
this.endTransactionThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getEndTransactionPoolQueueCapacity());
// broker統計信息
this.brokerStatsManager = new BrokerStatsManager(this.brokerConfig.getBrokerClusterName());
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的各種元素的實例化,主要來認識下4個元素
1.1 ConsumerOffsetManager
private ConcurrentMap<String/* topic@group */, ConcurrentMap<Integer, Long>> offsetTable =
new ConcurrentHashMap<String, ConcurrentMap<Integer, Long>>(512);
ConsumerOffsetManager有一個很重要的屬性就是offsetTable,key=topic@group,value=<消息隊列ID,offerset>,用于記錄topic下每個consumerGroup在每個隊列的消費進度
1.2 TopicConfigManager
private final ConcurrentMap<String, TopicConfig> topicConfigTable = new ConcurrentHashMap<String, TopicConfig>(1024);
鍵就是Topic,值TopicConfig用來記錄topic的配置信息(讀寫隊列數、讀寫權限、消息過濾方式等)
1.3 ConsumerManager
// 保存groupName對應的消費者組信息
private final ConcurrentMap<String/* Group */, ConsumerGroupInfo> consumerTable = new ConcurrentHashMap<String, ConsumerGroupInfo>(1024);
繼續看下ConsumerGroupInfo
private final ConcurrentMap<String/* Topic */, SubscriptionData> subscriptionTable = new ConcurrentHashMap<String, SubscriptionData>();
private final ConcurrentMap<Channel, ClientChannelInfo> channelInfoTable = new ConcurrentHashMap<Channel, ClientChannelInfo>(16);
private volatile ConsumeType consumeType;
private volatile MessageModel messageModel;
subscriptionTable 主要是記錄topic和訂閱信息
channelInfoTable 記錄Consumer的物理連接
ConsumeType是一個枚舉,表明兩種消費方式:
CONSUME_ACTIVELY("PULL"),
CONSUME_PASSIVELY("PUSH");
MessageModel 代表的是兩種消費模式:
BROADCASTING("BROADCASTING"),
CLUSTERING("CLUSTERING");
Broadcasting:同一個ConsumerGroup里的每個Consumer都能消費到所訂閱Topic的全部消息,也就是一個消息會被多次分發,被多個Consumer消費
Clustering:同一個ConsumerGroup里的每個Consumer只消費所訂閱消息的一部分內容,同一個ConsumerGroup里所有的Consumer消費的內容合起來才是所訂閱Topic內容的整體,從而達到負載均衡的目的
結合著來看,也就是說使用相同GroupName的一組Consumer,其ConsumeType和MessageModel必定相同,其訂閱的Topic會根據ConsumeType和MessageModel來完成相應的方式的消息處理
1.4 ProducerManager
private final ConcurrentHashMap<String /* group name */, ConcurrentHashMap<Channel, ClientChannelInfo>> groupChannelTable = new ConcurrentHashMap<>();
groupChannelTable 記錄group name 和物理連接的映射
2. BrokerController.initialize
public boolean initialize() throws CloneNotSupportedException {
// 文件加載
// 分別加載:topics.json、consumerOffset.json、
// subscriptionGroup.json、consumerFilter.json等4個文件到對應的manager對象中
boolean result = this.topicConfigManager.load();
result = result && this.consumerOffsetManager.load();
result = result && this.subscriptionGroupManager.load();
result = result && this.consumerFilterManager.load();
if (result) {
try {
this.messageStore =
new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener,
this.brokerConfig);
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);
// 加載messageStore的plugin,在執行messageStore之前先執行plugin
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);
}
}
// 文件加載和數據恢復
result = result && this.messageStore.load();
if (result) {
// 初始化netty服務端
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);
NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone();
fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2);
// 初始化VIP端口服務端,即在remotingServer監聽的端口號上-2
this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService);
// 線程池-處理從provider發送過來的消息
this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getSendMessageThreadPoolNums(),
this.brokerConfig.getSendMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.sendThreadPoolQueue,
new ThreadFactoryImpl("SendMessageThread_"));
// 線程池-處理從consumer來的消息拉取請求
this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getPullMessageThreadPoolNums(),
this.brokerConfig.getPullMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.pullThreadPoolQueue,
new ThreadFactoryImpl("PullMessageThread_"));
// 線程池-處理從consumer來的重試消息
this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.replyThreadPoolQueue,
new ThreadFactoryImpl("ProcessReplyMessageThread_"));
this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getQueryMessageThreadPoolNums(),
this.brokerConfig.getQueryMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.queryThreadPoolQueue,
new ThreadFactoryImpl("QueryMessageThread_"));
this.adminBrokerExecutor =
Executors.newFixedThreadPool(this.brokerConfig.getAdminBrokerThreadPoolNums(), new ThreadFactoryImpl(
"AdminBrokerThread_"));
// 線程池-客戶端管理,處理解除注冊、檢查配置事件
this.clientManageExecutor = new ThreadPoolExecutor(
this.brokerConfig.getClientManageThreadPoolNums(),
this.brokerConfig.getClientManageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.clientManagerThreadPoolQueue,
new ThreadFactoryImpl("ClientManageThread_"));
// 線程池-處理心跳事件
this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getHeartbeatThreadPoolNums(),
this.brokerConfig.getHeartbeatThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.heartbeatThreadPoolQueue,
new ThreadFactoryImpl("HeartbeatThread_", true));
this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getEndTransactionThreadPoolNums(),
this.brokerConfig.getEndTransactionThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.endTransactionThreadPoolQueue,
new ThreadFactoryImpl("EndTransactionThread_"));
this.consumerManageExecutor =
Executors.newFixedThreadPool(this.brokerConfig.getConsumerManageThreadPoolNums(), new ThreadFactoryImpl(
"ConsumerManageThread_"));
// 把事件類型、事件處理還有對應的線程池保存在processorTable中
this.registerProcessor();
final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis();
final long period = 1000 * 60 * 60 * 24;
// 每天凌晨打印昨天發送和拉取的消息數量
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.getBrokerStats().record();
} catch (Throwable e) {
log.error("schedule record error.", e);
}
}
}, initialDelay, period, TimeUnit.MILLISECONDS);
// 定時持久化consumerOffsetManager的內容到consumerOffset.json文件中
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerOffsetManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumerOffset error.", e);
}
}
}, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
// 定時持久化consumerOffsetManager的內容到consumerFilter.json文件中
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerFilterManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumer filter error.", e);
}
}
}, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
// 定時禁用消費慢的consumer(消息堆積量達到consumerFallbehindThreshold,默認16G),不允許consumer進行消息的拉取,
// 保護Broker,需要設置disableConsumeIfConsumerReadSlowly屬性,默認false
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.protectBroker();
} catch (Throwable e) {
log.error("protectBroker error.", e);
}
}
}, 3, 3, TimeUnit.MINUTES);
// 定時打印Send、Pull、Query、Transaction隊列信息
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.printWaterMark();
} catch (Throwable e) {
log.error("printWaterMark error.", e);
}
}
}, 10, 1, TimeUnit.SECONDS);
// 定時打印已存儲在提交日志中但尚未調度到消費隊列的字節數
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());
} catch (Throwable e) {
log.error("schedule dispatchBehindBytes error.", e);
}
}
}, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
// 如果設置了namesrvaddr的地址,則更新一次
// 否則定時更新namesrvaddr的地址
if (this.brokerConfig.getNamesrvAddr() != null) {
this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());
log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());
} else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.brokerOuterAPI.fetchNameServerAddr();
} catch (Throwable e) {
log.error("ScheduledTask fetchNameServerAddr exception", e);
}
}
}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
}
// 在非DLeger模式下
// 從節點根據配置更新主節點地址,主節點打印訂閱關系
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {
this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());
this.updateMasterHAServerAddrPeriodically = false;
} else {
this.updateMasterHAServerAddrPeriodically = true;
}
} else {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.printMasterAndSlaveDiff();
} catch (Throwable e) {
log.error("schedule printMasterAndSlaveDiff error.", e);
}
}
}, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
}
}
if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
// Register a listener to reload SslContext
try {
fileWatchService = new FileWatchService(
new String[] {
TlsSystemConfig.tlsServerCertPath,
TlsSystemConfig.tlsServerKeyPath,
TlsSystemConfig.tlsServerTrustCertPath
},
new FileWatchService.Listener() {
boolean certChanged, keyChanged = false;
@Override
public void onChanged(String path) {
if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {
log.info("The trust certificate changed, reload the ssl context");
reloadServerSslContext();
}
if (path.equals(TlsSystemConfig.tlsServerCertPath)) {
certChanged = true;
}
if (path.equals(TlsSystemConfig.tlsServerKeyPath)) {
keyChanged = true;
}
if (certChanged && keyChanged) {
log.info("The certificate and private key changed, reload the ssl context");
certChanged = keyChanged = false;
reloadServerSslContext();
}
}
private void reloadServerSslContext() {
((NettyRemotingServer) remotingServer).loadSslContext();
((NettyRemotingServer) fastRemotingServer).loadSslContext();
}
});
} catch (Exception e) {
log.warn("FileWatchService created error, can't load the certificate dynamically");
}
}
// 這里動態加載了TransactionalMessageService和AbstractTransactionalMessageCheckListener的實現類,位于如下
// “META-INF/service/org.apache.rocketmq.broker.transaction.TransactionalMessageService”
// “META-INF/service/org.apache.rocketmq.broker.transaction.AbstractTransactionalMessageCheckListener”
// 還創建了TransactionalMessageCheckService
initialTransaction();
// 加載"META-INF/service/org.apache.rocketmq.acl.AccessValidator"配置的AccessValidator實體類
// 然后將其包裝成RPC鉤子,注冊到remotingServer和fastRemotingServer中,用于請求的調用validate方法進行ACL權限檢查
// 創建ACL權限檢查
initialAcl();
// 加載"META-INF/service/org.apache.rocketmq.remoting.RPCHook"下的配置的實體類
initialRpcHooks();
}
return result;
}
這就是brokerController的初始化過程,接下來分成幾部分來拆解這個初始化過程
2.1 DefaultMessageStore的實例化
先來看看DefaultMessageStore的一些元素,以及它們的含義:
// 存儲相關的配置,例如存儲路徑、commitLog文件大小,刷盤頻次等等
private final MessageStoreConfig messageStoreConfig;
// comitLog 的核心處理類,消息存儲在 commitlog 文件中
private final CommitLog commitLog;
// topic 的隊列信息
private final ConcurrentMap<String/* topic */, ConcurrentMap<Integer/* queueId */, ConsumeQueue>> consumeQueueTable;
// ConsumeQueue 刷盤服務線程
private final FlushConsumeQueueService flushConsumeQueueService;
// commitLog 過期文件刪除線程
private final CleanCommitLogService cleanCommitLogService;
// consumeQueue 過期文件刪除線程
private final CleanConsumeQueueService cleanConsumeQueueService;
// 索引服務
private final IndexService indexService;
// MappedFile 分配線程,RocketMQ 使用內存映射處理 commitlog、consumeQueue文件
private final AllocateMappedFileService allocateMappedFileService;
// reput 轉發線程(負責 Commitlog 轉發到 Consumequeue、Index文件)
private final ReputMessageService reputMessageService;
// 主從同步實現服務
private final HAService haService;
// 定時任務調度器,執行定時任務
private final ScheduleMessageService scheduleMessageService;
// 存儲統計服務
private final StoreStatsService storeStatsService;
// ByteBuffer 池,后文會詳細使用
private final TransientStorePool transientStorePool;
// 存儲服務狀態
private final RunningFlags runningFlags = new RunningFlags();
// 獲取當前時鐘
private final SystemClock systemClock = new SystemClock();
private final ScheduledExecutorService scheduledExecutorService =
Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("StoreScheduledThread"));
// Broker 統計服務
private final BrokerStatsManager brokerStatsManager;
// 消息達到監聽器
private final MessageArrivingListener messageArrivingListener;
private final BrokerConfig brokerConfig;
private volatile boolean shutdown = true;
// 刷盤檢測點
private StoreCheckpoint storeCheckpoint;
private AtomicLong printTimes = new AtomicLong(0);
// 轉發 comitlog 日志,主要是從 commitlog 轉發到 consumeQueue、index 文件
private final LinkedList<CommitLogDispatcher> dispatcherList;
接下來看看它的初始化方法
public DefaultMessageStore(final MessageStoreConfig messageStoreConfig, final BrokerStatsManager brokerStatsManager,
final MessageArrivingListener messageArrivingListener, final BrokerConfig brokerConfig) throws IOException {
this.messageArrivingListener = messageArrivingListener;
this.brokerConfig = brokerConfig;
this.messageStoreConfig = messageStoreConfig;
this.brokerStatsManager = brokerStatsManager;
// 分配mappedFile服務,異步的創建和獲取mappedFile
this.allocateMappedFileService = new AllocateMappedFileService(this);
// 存儲服務
if (messageStoreConfig.isEnableDLegerCommitLog()) {
this.commitLog = new DLedgerCommitLog(this);
} else {
this.commitLog = new CommitLog(this);
}
// 消費隊列信息
this.consumeQueueTable = new ConcurrentHashMap<>(32);
// 刷新隊列服務,刷新consumerQueue的數據到磁盤上
this.flushConsumeQueueService = new FlushConsumeQueueService();
// 清除commitLog數據服務,刪除過期的物理文件
this.cleanCommitLogService = new CleanCommitLogService();
// 清除消費隊列服務,刪除consumerQueue中對應不上的邏輯文件(mappedFile)
this.cleanConsumeQueueService = new CleanConsumeQueueService();
this.storeStatsService = new StoreStatsService();
// 索引服務
this.indexService = new IndexService(this);
// HA服務,主從復制
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
this.haService = new HAService(this);
} else {
this.haService = null;
}
// 消息再分發服務
this.reputMessageService = new ReputMessageService();
// 主從節點切換服務
this.scheduleMessageService = new ScheduleMessageService(this);
this.transientStorePool = new TransientStorePool(messageStoreConfig);
if (messageStoreConfig.isTransientStorePoolEnable()) {
this.transientStorePool.init();
}
this.allocateMappedFileService.start();
this.indexService.start();
// ReputMessageService會調用dispatcherList中的每個分發類
// CommitLogDispatcherBuildConsumeQueue用于consumerQueue文件的構建
// CommitLogDispatcherBuildIndex用于index文件的構建
this.dispatcherList = new LinkedList<>();
this.dispatcherList.addLast(new CommitLogDispatcherBuildConsumeQueue());
this.dispatcherList.addLast(new CommitLogDispatcherBuildIndex());
// 構造文件鎖
File file = new File(StorePathConfigHelper.getLockFile(messageStoreConfig.getStorePathRootDir()));
MappedFile.ensureDirOK(file.getParent());
lockFile = new RandomAccessFile(file, "rw");
}
這里可以看到DefaultMessageStore會初始化很多的服務,來管理數據的存儲或是與磁盤交互。其中的部分服務會在后面的start啟動過程中講到,或者在producer的消息推送過程中講到。
2.2 this.messageStore.load()
public boolean load() {
boolean result = true;
try {
boolean lastExitOK = !this.isTempFileExist();
log.info("last shutdown {}", lastExitOK ? "normally" : "abnormally");
if (null != scheduleMessageService) {
result = result && this.scheduleMessageService.load();
}
// 加載 commitLog 文件到邏輯結構 mappedFile 中
result = result && this.commitLog.load();
// 保存 consumequeue 文件信息到consumeQueueTable中
result = result && this.loadConsumeQueue();
if (result) {
this.storeCheckpoint =
new StoreCheckpoint(StorePathConfigHelper.getStoreCheckpoint(this.messageStoreConfig.getStorePathRootDir()));
// 加載 index 文件到邏輯結構 IndexFile 中
this.indexService.load(lastExitOK);
// 數據恢復
// 1.重置consumerQueue和commitLog中每個mappedFile的buffer坐標
// 2.根據commitLog去掉consumerQueue中的臟數據
this.recover(lastExitOK);
log.info("load over, and the max phy offset = {}", this.getMaxPhyOffset());
}
} catch (Exception e) {
log.error("load exception", e);
result = false;
}
if (!result) {
this.allocateMappedFileService.shutdown();
}
return result;
}
這個方法主要就是將物理地址中的文件,包裝成對應的邏輯對象的過程,通過內存映射等原理操作邏輯地址。
2.3 初始化所需服務
回到brokerController的初始化方法中。接下來是初始化netty的連接服務以及各種線程池。
// 初始化netty服務端
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);
NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone();
fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2);
// 初始化VIP端口服務端
this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService);
// 線程池-處理從provider發送過來的消息
this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getSendMessageThreadPoolNums(),
this.brokerConfig.getSendMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.sendThreadPoolQueue,
new ThreadFactoryImpl("SendMessageThread_"));
// 線程池-處理從consumer來的消息拉取請求
this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getPullMessageThreadPoolNums(),
this.brokerConfig.getPullMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.pullThreadPoolQueue,
new ThreadFactoryImpl("PullMessageThread_"));
// 線程池-處理從consumer來的重試消息
this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.replyThreadPoolQueue,
new ThreadFactoryImpl("ProcessReplyMessageThread_"));
this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getQueryMessageThreadPoolNums(),
this.brokerConfig.getQueryMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.queryThreadPoolQueue,
new ThreadFactoryImpl("QueryMessageThread_"));
this.adminBrokerExecutor =
Executors.newFixedThreadPool(this.brokerConfig.getAdminBrokerThreadPoolNums(), new ThreadFactoryImpl(
"AdminBrokerThread_"));
// 線程池-客戶端管理,處理解除注冊、檢查配置事件
this.clientManageExecutor = new ThreadPoolExecutor(
this.brokerConfig.getClientManageThreadPoolNums(),
this.brokerConfig.getClientManageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.clientManagerThreadPoolQueue,
new ThreadFactoryImpl("ClientManageThread_"));
// 線程池-處理心跳事件
this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getHeartbeatThreadPoolNums(),
this.brokerConfig.getHeartbeatThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.heartbeatThreadPoolQueue,
new ThreadFactoryImpl("HeartbeatThread_", true));
this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getEndTransactionThreadPoolNums(),
this.brokerConfig.getEndTransactionThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.endTransactionThreadPoolQueue,
new ThreadFactoryImpl("EndTransactionThread_"));
this.consumerManageExecutor =
Executors.newFixedThreadPool(this.brokerConfig.getConsumerManageThreadPoolNums(), new ThreadFactoryImpl(
"ConsumerManageThread_"));
其中部分線程池在后續文章的分析中會再次見到
2.4 registerProcessor
這里主要是為 remotingServer 和 fastRemotingServer 注冊處理SendProcessor、ReplyMessageProcessor、NettyRequestProcessor、ClientManageProcessor、ConsumerManageProcessor、EndTransactionProcessor、AdminBrokerProcessor等事件處理器以及事件處理器對應的線程池。
不一樣的是PullMessageProcessor只能由 remotingServer 處理。
2.4 注冊定時器
// 每天凌晨打印昨天發送和拉取的消息數量
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.getBrokerStats().record();
} catch (Throwable e) {
log.error("schedule record error.", e);
}
}
}, initialDelay, period, TimeUnit.MILLISECONDS);
// 定時持久化consumerOffsetManager的內容到consumerOffset.json文件中
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerOffsetManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumerOffset error.", e);
}
}
}, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
// 定時持久化consumerOffsetManager的內容到consumerFilter.json文件中
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerFilterManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumer filter error.", e);
}
}
}, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
// 定時禁用消費慢的consumer(消息堆積量達到consumerFallbehindThreshold,默認16G),不允許consumer進行消息的拉取,
// 保護Broker,需要設置disableConsumeIfConsumerReadSlowly屬性,默認false
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.protectBroker();
} catch (Throwable e) {
log.error("protectBroker error.", e);
}
}
}, 3, 3, TimeUnit.MINUTES);
// 定時打印Send、Pull、Query、Transaction隊列信息
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.printWaterMark();
} catch (Throwable e) {
log.error("printWaterMark error.", e);
}
}
}, 10, 1, TimeUnit.SECONDS);
// 定時打印已存儲在提交日志中但尚未調度到消費隊列的字節數
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());
} catch (Throwable e) {
log.error("schedule dispatchBehindBytes error.", e);
}
}
}, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
// 如果設置了namesrvaddr的地址,則更新一次
// 否則定時更新namesrvaddr的地址
if (this.brokerConfig.getNamesrvAddr() != null) {
this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());
log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());
} else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.brokerOuterAPI.fetchNameServerAddr();
} catch (Throwable e) {
log.error("ScheduledTask fetchNameServerAddr exception", e);
}
}
}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
}
// 在非DLeger模式下
// 從節點根據配置更新主節點地址,主節點打印訂閱關系
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {
this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());
this.updateMasterHAServerAddrPeriodically = false;
} else {
this.updateMasterHAServerAddrPeriodically = true;
}
} else {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.printMasterAndSlaveDiff();
} catch (Throwable e) {
log.error("schedule printMasterAndSlaveDiff error.", e);
}
}
}, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
}
}
2.5 注冊和加載配置類
// 這里動態加載了TransactionalMessageService和AbstractTransactionalMessageCheckListener的實現類,位于如下
// “META-INF/service/org.apache.rocketmq.broker.transaction.TransactionalMessageService”
// “META-INF/service/org.apache.rocketmq.broker.transaction.AbstractTransactionalMessageCheckListener”
// 還創建了TransactionalMessageCheckService
initialTransaction();
// 加載"META-INF/service/org.apache.rocketmq.acl.AccessValidator"配置的AccessValidator實體類
// 然后將其包裝成RPC鉤子,注冊到remotingServer和fastRemotingServer中,用于請求的調用validate方法進行ACL權限檢查
// 創建ACL權限檢查
initialAcl();
// 加載"META-INF/service/org.apache.rocketmq.remoting.RPCHook"下的配置的實體類
initialRpcHooks();
至此BrokerController的創建和初始化過程就結束了,接下來是BrokerController的啟動過程。