本文首发于Ressmix个人站点:https://www.tpvlog.com
上一章,我们分析完了NameServer的启动流程,本章我们就来看下Broker的启动流程。整体上,Broker也是通过脚本启动,最终还是执行broker
模块下的BrokerStartup.main()
方法。
一、启动入口
1.1 BrokerStartup
我们先来看下BrokerStartup
的main方法,其实和NamesrvStartup
几乎是一个模子,都是先创建了一个Controller,然后启动它:
1public static void main(String[] args) { 2 // 创建BrokerController对象并启动 3 start(createBrokerController(args)); 4} 5 6public static BrokerController start(BrokerController controller) { 7 try { 8 9 // 启动BrokerController10 controller.start();1112 String tip = "The broker[" + controller.getBrokerConfig().getBrokerName() + ", "13 + controller.getBrokerAddr() + "] boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer();1415 if (null != controller.getBrokerConfig().getNamesrvAddr()) {16 tip += " and name server is " + controller.getBrokerConfig().getNamesrvAddr();17 }1819 log.info(tip);20 System.out.printf("%s%n", tip);21 return controller;22 } catch (Throwable e) {23 e.printStackTrace();24 System.exit(-1);25 }2627 return null;28}
上面关键是createBrokerController方法,我们就来分析下BrokerController是如何创建的。
二、创建BrokerController
createBrokerController
方法里面的代码非常长,我就省略一些无关紧要的代码了:
1public static BrokerController createBrokerController(String[] args) { 2 3 // ...省略Netty缓存区配置的代码 4 5 try { 6 // 解析命令行参数,和NamesrvController里面的操作类似 7 Options options = ServerUtil.buildCommandlineOptions(new Options()); 8 commandLine = ServerUtil.parseCmdLine("mqbroker", args, buildCommandlineOptions(options), 9 new PosixParser()); 10 if (null == commandLine) { 11 System.exit(-1); 12 } 13 14 // 这里是关键,构建了Broker自身的配置对象、底层的Netty服务端配置、Netty客户端配置 15 final BrokerConfig brokerConfig = new BrokerConfig(); 16 final NettyServerConfig nettyServerConfig = new NettyServerConfig(); 17 final NettyClientConfig nettyClientConfig = new NettyClientConfig(); 18 19 nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TLS_ENABLE, 20 String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING)))); 21 22 // Netty服务端监听10911端口 23 nettyServerConfig.setListenPort(10911); 24 25 // Broker用来存储消息的配置 26 final MessageStoreConfig messageStoreConfig = new MessageStoreConfig(); 27 28 // Slave-Broker的参数设置 29 if (BrokerRole.SLAVE == messageStoreConfig.getBrokerRole()) { 30 int ratio = messageStoreConfig.getAccessMessageInMemoryMaxRatio() - 10; 31 messageStoreConfig.setAccessMessageInMemoryMaxRatio(ratio); 32 } 33 34 // ...省略解析命令行文件配置的代码 35 36 // 解析配置中的NameServer地址 37 String namesrvAddr = brokerConfig.getNamesrvAddr(); 38 if (null != namesrvAddr) { 39 try { 40 String[] addrArray = namesrvAddr.split(";"); 41 for (String addr : addrArray) { 42 RemotingUtil.string2SocketAddress(addr); 43 } 44 } catch (Exception e) { 45 System.out.printf( 46 "The Name Server Address[%s] illegal, please set it as follows, \"127.0.0.1:9876;192.168.0.1:9876\"%n", 47 namesrvAddr); 48 System.exit(-3); 49 } 50 } 51 52 // 判断Broker的角色,不同角色用不同的特性配置值 53 switch (messageStoreConfig.getBrokerRole()) { 54 case ASYNC_MASTER: 55 case SYNC_MASTER: 56 brokerConfig.setBrokerId(MixAll.MASTER_ID); 57 break; 58 case SLAVE: 59 if (brokerConfig.getBrokerId() <= 0) { 60 System.out.printf("Slave's brokerId must be > 0"); 61 System.exit(-3); 62 } 63 64 break; 65 default: 66 break; 67 } 68 69 // 判断是否开启DLeger机制 70 if (messageStoreConfig.isEnableDLegerCommitLog()) { 71 brokerConfig.setBrokerId(-1); 72 } 73 74 // 设置HA监听端口号,这个暂时不管,我们后面会专门讲 75 messageStoreConfig.setHaListenPort(nettyServerConfig.getListenPort() + 1); 76 77 // ...省略日志配置的代码 78 79 // 创建一个BrokerController对象 80 final BrokerController controller = new BrokerController( 81 brokerConfig, 82 nettyServerConfig, 83 nettyClientConfig, 84 messageStoreConfig); 85 // remember all configs to prevent discard 86 controller.getConfiguration().registerConfig(properties); 87 88 // 初始化BrokerController对象 89 boolean initResult = controller.initialize(); 90 if (!initResult) { 91 controller.shutdown(); 92 System.exit(-3); 93 } 94 95 // ...省略shutdownhook代码 96 97 return controller; 98 } catch (Throwable e) { 99 e.printStackTrace();100 System.exit(-1);101 }102103 return null;104}
从上述代码可以看出,创建BrokerController时,核心就做了两件事情:
解析各种配置,创建BrokerController需要的各种配置对象:BrokerConfig、NettyServerConfig、NettyClientConfig、MessageStoreConfig;
调用BrokerController对象的initialize方法,进行初始化。
2.1 BrokerController配置
从创建BrokerController的代码中,我们可以看出,BrokerController依赖的四个核心配置如下:
这些配置其实没什么好说的,就是一些普通的POJO类。所以此时,Broker的整个组件架构应该是这样的:
Broker这个JVM进程运行期间,都是由BrokerController这个管控组件去管理Broker的请求处理、后台线程以及磁盘数据。
2.3 BrokerController对象
在我们继续研究BrokerController如何进行初始化之前,先要来看下BrokerController内部到底是什么样的。BrokerController其实就是一个Broker管控组件,它控制着当前运行的这个Broker的行为,包括接收网络请求、管理磁盘上的消息数据,以及一大堆的后台线程的运行。
我们来看下BrokerController的构造函数,里面创建了各种各样的组件:
1public BrokerController( 2 final BrokerConfig brokerConfig, 3 final NettyServerConfig nettyServerConfig, 4 final NettyClientConfig nettyClientConfig, 5 final MessageStoreConfig messageStoreConfig 6) { 7 // BrokerController的一些核心配置 8 this.brokerConfig = brokerConfig; 9 this.nettyServerConfig = nettyServerConfig;10 this.nettyClientConfig = nettyClientConfig;11 this.messageStoreConfig = messageStoreConfig;1213 // 下面是Broker内部的各个功能组件,这些组件全部由BrokerController掌控14 this.consumerOffsetManager = new ConsumerOffsetManager(this);15 this.topicConfigManager = new TopicConfigManager(this);16 this.pullMessageProcessor = new PullMessageProcessor(this);17 this.pullRequestHoldService = new PullRequestHoldService(this);18 this.messageArrivingListener = new NotifyMessageArrivingListener(this.pullRequestHoldService);19 this.consumerIdsChangeListener = new DefaultConsumerIdsChangeListener(this);20 this.consumerManager = new ConsumerManager(this.consumerIdsChangeListener);21 this.consumerFilterManager = new ConsumerFilterManager(this);22 this.producerManager = new ProducerManager();23 this.clientHousekeepingService = new ClientHousekeepingService(this);24 this.broker2Client = new Broker2Client(this);25 this.subscriptionGroupManager = new SubscriptionGroupManager(this);26 this.brokerOuterAPI = new BrokerOuterAPI(nettyClientConfig);27 this.filterServerManager = new FilterServerManager(this);28 this.slaveSynchronize = new SlaveSynchronize(this);2930 // 各种线程池队列31 this.sendThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getSendThreadPoolQueueCapacity());32 this.pullThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getPullThreadPoolQueueCapacity());33 this.replyThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getReplyThreadPoolQueueCapacity());34 this.queryThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getQueryThreadPoolQueueCapacity());35 this.clientManagerThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getClientManagerThreadPoolQueueCapacity());36 this.consumerManagerThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getConsumerManagerThreadPoolQueueCapacity());37 this.heartbeatThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getHeartbeatThreadPoolQueueCapacity());38 this.endTransactionThreadPoolQueue = new LinkedBlockingQueue(this.brokerConfig.getEndTransactionPoolQueueCapacity());3940 // metric统计组件41 this.brokerStatsManager = new BrokerStatsManager(this.brokerConfig.getBrokerClusterName());4243 this.setStoreHost(new InetSocketAddress(this.getBrokerConfig().getBrokerIP1(), this.getNettyServerConfig().getListenPort()));4445 // 处理Broker故障的组件46 this.brokerFastFailure = new BrokerFastFailure(this);47 this.configuration = new Configuration(48 log,49 BrokerPathConfigHelper.getBrokerConfigPath(),50 this.brokerConfig, this.nettyServerConfig, this.nettyClientConfig, this.messageStoreConfig51 );52}
上面的各种组件没必要在阅读BrokerController源码时一下子扎进去读,只要大体知道它们是做什么的的就可以了,后面可以针对Broker的某项功能再去深入研究:
2.4 初始化BrokerController
现在BrokerController相关的配置都解析好了,BrokerController对象也创建好了,接下来就要对BrokerController进行初始化了:
1public boolean initialize() throws CloneNotSupportedException { 2 // 加载Topic配置 3 boolean result = this.topicConfigManager.load(); 4 // 加载Consumer的消费ofset 5 result = result && this.consumerOffsetManager.load(); 6 // 加载Consumer订阅组 7 result = result && this.subscriptionGroupManager.load(); 8 // 加载过滤器 9 result = result && this.consumerFilterManager.load(); 10 11 if (result) { 12 try { 13 // 创建消息存储管理组件 14 this.messageStore = 15 new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener, 16 this.brokerConfig); 17 18 // 如果启用了DLeger机制,就初始化一堆DLeger相关组件 19 if (messageStoreConfig.isEnableDLegerCommitLog()) { 20 DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore); 21 ((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler); 22 } 23 24 // 创建Broker统计组件 25 this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore); 26 27 // 下面这坨暂时不用管 28 MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig); 29 this.messageStore = MessageStoreFactory.build(context, this.messageStore); 30 this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager)); 31 } catch (IOException e) { 32 result = false; 33 log.error("Failed to initialize", e); 34 } 35 } 36 result = result && this.messageStore.load(); 37 38 if (result) { 39 // 这里很关键,创建了NettyRemotingServer,作为底层的Netty服务器,接受客户端请求 40 this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService); 41 NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone(); 42 fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2); 43 this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService); 44 45 // 下面是创建各种线程池,主要有两类:第一类负责处理别人发过来的请求,第二类负责处理自己的一些后台任务 46 this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor( 47 this.brokerConfig.getSendMessageThreadPoolNums(), 48 this.brokerConfig.getSendMessageThreadPoolNums(), 49 1000 * 60, 50 TimeUnit.MILLISECONDS, 51 this.sendThreadPoolQueue, 52 new ThreadFactoryImpl("SendMessageThread_")); 53 54 this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor( 55 this.brokerConfig.getPullMessageThreadPoolNums(), 56 this.brokerConfig.getPullMessageThreadPoolNums(), 57 1000 * 60, 58 TimeUnit.MILLISECONDS, 59 this.pullThreadPoolQueue, 60 new ThreadFactoryImpl("PullMessageThread_")); 61 62 this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor( 63 this.brokerConfig.getProcessReplyMessageThreadPoolNums(), 64 this.brokerConfig.getProcessReplyMessageThreadPoolNums(), 65 1000 * 60, 66 TimeUnit.MILLISECONDS, 67 this.replyThreadPoolQueue, 68 new ThreadFactoryImpl("ProcessReplyMessageThread_")); 69 70 this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor( 71 this.brokerConfig.getQueryMessageThreadPoolNums(), 72 this.brokerConfig.getQueryMessageThreadPoolNums(), 73 1000 * 60, 74 TimeUnit.MILLISECONDS, 75 this.queryThreadPoolQueue, 76 new ThreadFactoryImpl("QueryMessageThread_")); 77 78 // 这是一个管理Broker命令执行的线程池 79 this.adminBrokerExecutor = 80 Executors.newFixedThreadPool(this.brokerConfig.getAdminBrokerThreadPoolNums(), new ThreadFactoryImpl( 81 "AdminBrokerThread_")); 82 83 // 管理客户端的线程池 84 this.clientManageExecutor = new ThreadPoolExecutor( 85 this.brokerConfig.getClientManageThreadPoolNums(), 86 this.brokerConfig.getClientManageThreadPoolNums(), 87 1000 * 60, 88 TimeUnit.MILLISECONDS, 89 this.clientManagerThreadPoolQueue, 90 new ThreadFactoryImpl("ClientManageThread_")); 91 92 // 这个线程池负责给NameServer发送心跳 93 this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor( 94 this.brokerConfig.getHeartbeatThreadPoolNums(), 95 this.brokerConfig.getHeartbeatThreadPoolNums(), 96 1000 * 60, 97 TimeUnit.MILLISECONDS, 98 this.heartbeatThreadPoolQueue, 99 new ThreadFactoryImpl("HeartbeatThread_", true));100101 // 这是一个跟事务消息有关的线程池102 this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor(103 this.brokerConfig.getEndTransactionThreadPoolNums(),104 this.brokerConfig.getEndTransactionThreadPoolNums(),105 1000 * 60,106 TimeUnit.MILLISECONDS,107 this.endTransactionThreadPoolQueue,108 new ThreadFactoryImpl("EndTransactionThread_"));109110 // 管理Consumer的线程池111 this.consumerManageExecutor =112 Executors.newFixedThreadPool(this.brokerConfig.getConsumerManageThreadPoolNums(), new ThreadFactoryImpl(113 "ConsumerManageThread_"));114115 this.registerProcessor();116117 // 下面都是定时调度线程池的后台执行118 final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis();119 final long period = 1000 * 60 * 60 * 24;120 // 定时调度Metric统计任务121 this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {122 @Override123 public void run() {124 try {125 BrokerController.this.getBrokerStats().record();126 } catch (Throwable e) {127 log.error("schedule record error.", e);128 }129 }130 }, initialDelay, period, TimeUnit.MILLISECONDS);131132 // ...省略其它各种定时调度任务133134 // 下面是设置NameServer地址,支持各种配置方式135 if (this.brokerConfig.getNamesrvAddr() != null) {136 this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());137 log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());138 } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {139 this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {140141 @Override142 public void run() {143 try {144 BrokerController.this.brokerOuterAPI.fetchNameServerAddr();145 } catch (Throwable e) {146 log.error("ScheduledTask fetchNameServerAddr exception", e);147 }148 }149 }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);150 }151152 // 下面这些代码跟DLeger机制有关,暂时也不用管153 if (!messageStoreConfig.isEnableDLegerCommitLog()) {154 if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {155 if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {156 this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());157 this.updateMasterHAServerAddrPeriodically = false;158 } else {159 this.updateMasterHAServerAddrPeriodically = true;160 }161 } else {162 this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {163 @Override164 public void run() {165 try {166 BrokerController.this.printMasterAndSlaveDiff();167 } catch (Throwable e) {168 log.error("schedule printMasterAndSlaveDiff error.", e);169 }170 }171 }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);172 }173 }174175 // ...省略其它无关代码176177 // 初始化事务消息相关的东西178 initialTransaction();179 // 初始化权限控制相关的东西180 initialAcl();181 // 不用管182 initialRpcHooks();183 }184 return result;185}
很多童鞋看到上面的代码估计都要晕了,其实里面很多东西我们没必要去深究,我们要弄清楚的就是BrokerController的整个启动流程,上述代码的核心逻辑就是:
对BrokerController内部的各种组件进行配置加载;
创建一些额外组件(包括核心的NettyRemotingServer);
创建各种线程池。
最核心的,就是BrokerController一旦初始化完成后,就准备好了Netty服务器,可以用于接收网络请求,然后准备好了处理各种请求的线程池,以及各种用于执行后台定时任务的线程池。
三、启动BrokerController
到这里,BrokerController已经完成了初始化:
实现各种功能的核心组件都已经初始化完毕;
底层的Netty服务器也初始化完毕;
负责处理请求的线程池以及执行定时调度任务的线程池也都初始化完毕。
最后,就要调用BrokerController对象的start方法进行启动了。BrokerController的启动,会正式完成内部Netty服务器的启动,然后就可以接收客户端请求了,同时Broker也可以作为Netty客户端向NameServer进行注册以及保持心跳。
从start方法可以看到,其实就是对BrokerController内部的各个组件执行它们自己的start方法,挨个去启动它们:
1public void start() throws Exception { 2 if (this.messageStore != null) { 3 this.messageStore.start(); 4 } 5 6 // 启动Netty服务器 7 if (this.remotingServer != null) { 8 this.remotingServer.start(); 9 }1011 if (this.fastRemotingServer != null) {12 this.fastRemotingServer.start();13 }1415 if (this.fileWatchService != null) {16 this.fileWatchService.start();17 }1819 // 这里比较关键,因为Broker要发送心跳、注册请求到NameServer,所以它自身也必须是一个Netty客户端,所以这里其实就是启动一个Netty客户端组件20 if (this.brokerOuterAPI != null) {21 this.brokerOuterAPI.start();22 }2324 if (this.pullRequestHoldService != null) {25 this.pullRequestHoldService.start();26 }2728 if (this.clientHousekeepingService != null) {29 this.clientHousekeepingService.start();30 }3132 if (this.filterServerManager != null) {33 this.filterServerManager.start();34 }3536 if (!messageStoreConfig.isEnableDLegerCommitLog()) {37 startProcessorByHa(messageStoreConfig.getBrokerRole());38 handleSlaveSynchronize(messageStoreConfig.getBrokerRole());39 this.registerBrokerAll(true, false, true);40 }4142 // 这里很关键,用于向线程池提交一个任务,这个任务的作用就是将当前Broker注册到NameServer43 this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {4445 @Override46 public void run() {47 try {48 BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());49 } catch (Throwable e) {50 log.error("registerBrokerAll Exception", e);51 }52 }53 }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);5455 if (this.brokerStatsManager != null) {56 this.brokerStatsManager.start();57 }5859 if (this.brokerFastFailure != null) {60 this.brokerFastFailure.start();61 }62}
上面的代码核心就是三块:
启动Netty服务器,用于接收各种客户端的网络请求;
启动一个BrokerOuterAPI组件,这个组件可以基于Netty客户端发送请求给别人;
启动一个线程,将当前Broker注册到NameServer。
四、总结
本章,我详细讲解了Broker的整个启动流程,在阅读Broker和Nameserver的源码过程中,我们也可以体会到,一定要抓住开源框架运行的主体流程和核心组件,而不要陷入各种组件的细节里去。
对于各种细节代码,可以后续从各种场景驱动去翻阅源码,比如Broker的注册和心跳,Producer从NameServer拉取路由信息,Producer根据负载均衡算法选择一个Broker机器,Broker把消息存储到磁盘等等,这样,在阅读源码的过程中才不会半途而废。