RocketMQ中Broker的启动源码分析(一)

  • 2019 年 10 月 3 日
  • 笔记

在RocketMQ中,使用BrokerStartup作为启动类,相较于NameServer的启动,Broker作为RocketMQ的核心可复杂得多

【RocketMQ中NameServer的启动源码分析】

主函数作为其启动的入口:

1 public static void main(String[] args) {  2     start(createBrokerController(args));  3 }

首先通过createBrokerController方法生成Broker的控制器BrokerController

createBrokerController方法:

  1 public static BrokerController createBrokerController(String[] args) {    2     System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));    3    4     if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_SNDBUF_SIZE)) {    5         NettySystemConfig.socketSndbufSize = 131072;    6     }    7    8     if (null == System.getProperty(NettySystemConfig.COM_ROCKETMQ_REMOTING_SOCKET_RCVBUF_SIZE)) {    9         NettySystemConfig.socketRcvbufSize = 131072;   10     }   11   12     try {   13         //PackageConflictDetect.detectFastjson();   14         Options options = ServerUtil.buildCommandlineOptions(new Options());   15         commandLine = ServerUtil.parseCmdLine("mqbroker", args, buildCommandlineOptions(options),   16             new PosixParser());   17         if (null == commandLine) {   18             System.exit(-1);   19         }   20   21         final BrokerConfig brokerConfig = new BrokerConfig();   22         final NettyServerConfig nettyServerConfig = new NettyServerConfig();   23         final NettyClientConfig nettyClientConfig = new NettyClientConfig();   24   25         nettyClientConfig.setUseTLS(Boolean.parseBoolean(System.getProperty(TLS_ENABLE,   26             String.valueOf(TlsSystemConfig.tlsMode == TlsMode.ENFORCING))));   27         nettyServerConfig.setListenPort(10911);   28         final MessageStoreConfig messageStoreConfig = new MessageStoreConfig();   29   30         if (BrokerRole.SLAVE == messageStoreConfig.getBrokerRole()) {   31             int ratio = messageStoreConfig.getAccessMessageInMemoryMaxRatio() - 10;   32             messageStoreConfig.setAccessMessageInMemoryMaxRatio(ratio);   33         }   34   35         if (commandLine.hasOption('c')) {   36             String file = commandLine.getOptionValue('c');   37             if (file != null) {   38                 configFile = file;   39                 InputStream in = new BufferedInputStream(new FileInputStream(file));   40                 properties = new Properties();   41                 properties.load(in);   42   43                 properties2SystemEnv(properties);   44                 MixAll.properties2Object(properties, brokerConfig);   45                 MixAll.properties2Object(properties, nettyServerConfig);   46                 MixAll.properties2Object(properties, nettyClientConfig);   47                 MixAll.properties2Object(properties, messageStoreConfig);   48   49                 BrokerPathConfigHelper.setBrokerConfigPath(file);   50                 in.close();   51             }   52         }   53   54         MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), brokerConfig);   55   56         if (null == brokerConfig.getRocketmqHome()) {   57             System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation", MixAll.ROCKETMQ_HOME_ENV);   58             System.exit(-2);   59         }   60   61         String namesrvAddr = brokerConfig.getNamesrvAddr();   62         if (null != namesrvAddr) {   63             try {   64                 String[] addrArray = namesrvAddr.split(";");   65                 for (String addr : addrArray) {   66                     RemotingUtil.string2SocketAddress(addr);   67                 }   68             } catch (Exception e) {   69                 System.out.printf(   70                     "The Name Server Address[%s] illegal, please set it as follows, "127.0.0.1:9876;192.168.0.1:9876"%n",   71                     namesrvAddr);   72                 System.exit(-3);   73             }   74         }   75   76         switch (messageStoreConfig.getBrokerRole()) {   77             case ASYNC_MASTER:   78             case SYNC_MASTER:   79                 brokerConfig.setBrokerId(MixAll.MASTER_ID);   80                 break;   81             case SLAVE:   82                 if (brokerConfig.getBrokerId() <= 0) {   83                     System.out.printf("Slave's brokerId must be > 0");   84                     System.exit(-3);   85                 }   86   87                 break;   88             default:   89                 break;   90         }   91   92         messageStoreConfig.setHaListenPort(nettyServerConfig.getListenPort() + 1);   93         LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();   94         JoranConfigurator configurator = new JoranConfigurator();   95         configurator.setContext(lc);   96         lc.reset();   97         configurator.doConfigure(brokerConfig.getRocketmqHome() + "/conf/logback_broker.xml");   98   99         if (commandLine.hasOption('p')) {  100             InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);  101             MixAll.printObjectProperties(console, brokerConfig);  102             MixAll.printObjectProperties(console, nettyServerConfig);  103             MixAll.printObjectProperties(console, nettyClientConfig);  104             MixAll.printObjectProperties(console, messageStoreConfig);  105             System.exit(0);  106         } else if (commandLine.hasOption('m')) {  107             InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);  108             MixAll.printObjectProperties(console, brokerConfig, true);  109             MixAll.printObjectProperties(console, nettyServerConfig, true);  110             MixAll.printObjectProperties(console, nettyClientConfig, true);  111             MixAll.printObjectProperties(console, messageStoreConfig, true);  112             System.exit(0);  113         }  114  115         log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);  116         MixAll.printObjectProperties(log, brokerConfig);  117         MixAll.printObjectProperties(log, nettyServerConfig);  118         MixAll.printObjectProperties(log, nettyClientConfig);  119         MixAll.printObjectProperties(log, messageStoreConfig);  120  121         final BrokerController controller = new BrokerController(  122             brokerConfig,  123             nettyServerConfig,  124             nettyClientConfig,  125             messageStoreConfig);  126         // remember all configs to prevent discard  127         controller.getConfiguration().registerConfig(properties);  128  129         boolean initResult = controller.initialize();  130         if (!initResult) {  131             controller.shutdown();  132             System.exit(-3);  133         }  134  135         Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {  136             private volatile boolean hasShutdown = false;  137             private AtomicInteger shutdownTimes = new AtomicInteger(0);  138  139             @Override  140             public void run() {  141                 synchronized (this) {  142                     log.info("Shutdown hook was invoked, {}", this.shutdownTimes.incrementAndGet());  143                     if (!this.hasShutdown) {  144                         this.hasShutdown = true;  145                         long beginTime = System.currentTimeMillis();  146                         controller.shutdown();  147                         long consumingTimeTotal = System.currentTimeMillis() - beginTime;  148                         log.info("Shutdown hook over, consuming total time(ms): {}", consumingTimeTotal);  149                     }  150                 }  151             }  152         }, "ShutdownHook"));  153  154         return controller;  155     } catch (Throwable e) {  156         e.printStackTrace();  157         System.exit(-1);  158     }  159  160     return null;  161 }

这里和NameServer中的createNamesrvController方法作用类似,对Broker所需做了一系列的配置

先设置了Netty通信时的缓冲区大小,这里默认是128K

接着会创建了几个实体类
BrokerConfig,用来封装其绝大多数基本配置信息
NettyServerConfig,封装了其作为对外暴露的消息队列服务器的信息
NettyClientConfig,则封装了其作为NameServer客户端的信息
这里面封装的信息和NameServer一个道理,都是映射了配置文件相应的配置

然后对NettyClientConfig的TLS进行设置
让NettyServerConfig默认监听10911端口

紧接着创建了一个MessageStoreConfig,这个就是用来封装Store的信息,

MessageStoreConfig会默认配置BrokerRole为ASYNC_MASTER
Broker有三种身份,用BrokerRole枚举来表示:

1 public enum BrokerRole {  2     ASYNC_MASTER,  3     SYNC_MASTER,  4     SLAVE;  5 }

也就是异步Master,同步Master,以及Slave

这里会对其身份进行检查,若是Slave,则需要调整其允许的消息最大内存占比,默认值是40,也就是说Master允许消息最大内存占用40%,而Slave则只允许30%

接着会对”-c“指令进行相应配置的加载

往后看到对namesrvAddr进行了检查,只是简单地检查NameServer集群地址信息是否合法

往下看到有个switch块,其根据Broker的身份,进行设置
只要是Master,将其BrokerId设为0,而Slave的BrokerId需要大于0
(一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,大于0表示Slave,Master也可以部署多个)

继续往下,这里会对Store设置HA的监听端口,是NettyServer侦听端口加1

往下是对“-p”,”-m“指令进行相应配置的加载,以及日志的相关配置

之后就会创建了一个BrokerController:

 1 public BrokerController(   2         final BrokerConfig brokerConfig,   3         final NettyServerConfig nettyServerConfig,   4         final NettyClientConfig nettyClientConfig,   5         final MessageStoreConfig messageStoreConfig   6     ) {   7     this.brokerConfig = brokerConfig;   8     this.nettyServerConfig = nettyServerConfig;   9     this.nettyClientConfig = nettyClientConfig;  10     this.messageStoreConfig = messageStoreConfig;  11     this.consumerOffsetManager = new ConsumerOffsetManager(this);  12     this.topicConfigManager = new TopicConfigManager(this);  13     this.pullMessageProcessor = new PullMessageProcessor(this);  14     this.pullRequestHoldService = new PullRequestHoldService(this);  15     this.messageArrivingListener = new NotifyMessageArrivingListener(this.pullRequestHoldService);  16     this.consumerIdsChangeListener = new DefaultConsumerIdsChangeListener(this);  17     this.consumerManager = new ConsumerManager(this.consumerIdsChangeListener);  18     this.consumerFilterManager = new ConsumerFilterManager(this);  19     this.producerManager = new ProducerManager();  20     this.clientHousekeepingService = new ClientHousekeepingService(this);  21     this.broker2Client = new Broker2Client(this);  22     this.subscriptionGroupManager = new SubscriptionGroupManager(this);  23     this.brokerOuterAPI = new BrokerOuterAPI(nettyClientConfig);  24     this.filterServerManager = new FilterServerManager(this);  25  26     this.slaveSynchronize = new SlaveSynchronize(this);  27  28     this.sendThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getSendThreadPoolQueueCapacity());  29     this.pullThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getPullThreadPoolQueueCapacity());  30     this.queryThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getQueryThreadPoolQueueCapacity());  31     this.clientManagerThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getClientManagerThreadPoolQueueCapacity());  32     this.consumerManagerThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getConsumerManagerThreadPoolQueueCapacity());  33     this.heartbeatThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getHeartbeatThreadPoolQueueCapacity());  34     this.endTransactionThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getEndTransactionPoolQueueCapacity());  35  36     this.brokerStatsManager = new BrokerStatsManager(this.brokerConfig.getBrokerClusterName());  37     this.setStoreHost(new InetSocketAddress(this.getBrokerConfig().getBrokerIP1(), this.getNettyServerConfig().getListenPort()));  38  39     this.brokerFastFailure = new BrokerFastFailure(this);  40     this.configuration = new Configuration(  41         log,  42         BrokerPathConfigHelper.getBrokerConfigPath(),  43         this.brokerConfig, this.nettyServerConfig, this.nettyClientConfig, this.messageStoreConfig  44     );  45 }

可以看到,这里实例化了许多成员,我就不一一分析,挑几个重要的介绍

 

ConsumerOffsetManager:用来管理消费者的消费消息的进度,主要通过一张map来缓存

1 private ConcurrentMap<String/* topic@group */, ConcurrentMap<Integer, Long>> offsetTable =  2         new ConcurrentHashMap<String, ConcurrentMap<Integer, Long>>(512);

由topic@group的形式构成键,而值中的map的Integer代表具体的哪条消息队列,Long表示该消息队列的偏移量offset

 

TopicConfigManager:管理Topic和消息队列的信息,主要通过一张map来缓存

1 private final ConcurrentMap<String, TopicConfig> topicConfigTable =  2         new ConcurrentHashMap<String, TopicConfig>(1024);  3     private final DataVersion dataVersion = new DataVersion();

键就是Topic,值TopicConfig用来记录对应的消息队列的个数

PullMessageProcessor、PullRequestHoldService、NotifyMessageArrivingListener这三个来管理Pull消息请求,关于Pull消息在后续博客再细说

ConsumerManager:管理Consumer,主要通过一张map来缓存

1 private final ConcurrentMap<String/* Group */, ConsumerGroupInfo> consumerTable =  2         new ConcurrentHashMap<String, ConsumerGroupInfo>(1024);

键值就是Consumer的GroupName,
而ConsumerGroupInfo由如下构成:

1 private final ConcurrentMap<String/* Topic */, SubscriptionData> subscriptionTable =  2         new ConcurrentHashMap<String, SubscriptionData>();  3 private final ConcurrentMap<Channel, ClientChannelInfo> channelInfoTable =  4 new ConcurrentHashMap<Channel, ClientChannelInfo>(16);  5 private volatile ConsumeType consumeType;  6 private volatile MessageModel messageModel;        

可以看到封装了一个subscriptionTable ,这个map记录Topic和订阅内容
以及一个channelInfoTable,记录Consumer的物理连接
ConsumeType是一个枚举,表明两种消费方式:

1 public enum ConsumeType {  2     CONSUME_ACTIVELY("PULL"),  3     CONSUME_PASSIVELY("PUSH");  4 }

MessageModel 也是一个枚举,表明两种消费模式:

 1 public enum MessageModel {   2     /**   3      * broadcast   4      */   5     BROADCASTING("BROADCASTING"),   6     /**   7      * clustering   8      */   9     CLUSTERING("CLUSTERING");  10 }

Broadcasting:同一个ConsumerGroup里的每个Consumer都能消费到所订阅Topic的全部消息,也就是一个消息会被多次分发,被多个Consumer消费
Clustering:同一个ConsumerGroup里的每个Consumer只消费所订阅消息的一部分内容,同一个ConsumerGroup里所有的Consumer消费的内容合起来才是所订阅Topic内容的整体,从而达到负载均衡的目的

结合着来看,也就是说使用相同GroupName的一组Consumer,其ConsumeType和MessageModel必定相同,其订阅的Topic会根据ConsumeType和MessageModel来完成相应的方式的消息处理

回到BrokerController的构造

ProducerManager:管理Producer,主要通过一张map来缓存

1 private final HashMap<String /* group name */, HashMap<Channel, ClientChannelInfo>> groupChannelTable =  2         new HashMap<String, HashMap<Channel, ClientChannelInfo>>();

相比ConsumerManager,对Producer的管理简单的多,只需要记录group name 和物理连接的映射

 

再回到createBrokerController方法,在完成BrokerController的创建后,会调用BrokerController的initialize方法:

BrokerController的initialize方法:

  1 public boolean initialize() throws CloneNotSupportedException {    2     boolean result = this.topicConfigManager.load();    3    4     result = result && this.consumerOffsetManager.load();    5     result = result && this.subscriptionGroupManager.load();    6     result = result && this.consumerFilterManager.load();    7    8     if (result) {    9         try {   10             this.messageStore =   11                 new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener,   12                     this.brokerConfig);   13             if (messageStoreConfig.isEnableDLegerCommitLog()) {   14                 DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore);   15                 ((DLedgerCommitLog)((DefaultMessageStore) messageStore).getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler);   16             }   17             this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore);   18             //load plugin   19             MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig);   20             this.messageStore = MessageStoreFactory.build(context, this.messageStore);   21             this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));   22         } catch (IOException e) {   23             result = false;   24             log.error("Failed to initialize", e);   25         }   26     }   27   28     result = result && this.messageStore.load();   29   30     if (result) {   31         this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);   32         NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone();   33         fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2);   34         this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService);   35         this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor(   36             this.brokerConfig.getSendMessageThreadPoolNums(),   37             this.brokerConfig.getSendMessageThreadPoolNums(),   38             1000 * 60,   39             TimeUnit.MILLISECONDS,   40             this.sendThreadPoolQueue,   41             new ThreadFactoryImpl("SendMessageThread_"));   42   43         this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor(   44             this.brokerConfig.getPullMessageThreadPoolNums(),   45             this.brokerConfig.getPullMessageThreadPoolNums(),   46             1000 * 60,   47             TimeUnit.MILLISECONDS,   48             this.pullThreadPoolQueue,   49             new ThreadFactoryImpl("PullMessageThread_"));   50   51         this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor(   52             this.brokerConfig.getQueryMessageThreadPoolNums(),   53             this.brokerConfig.getQueryMessageThreadPoolNums(),   54             1000 * 60,   55             TimeUnit.MILLISECONDS,   56             this.queryThreadPoolQueue,   57             new ThreadFactoryImpl("QueryMessageThread_"));   58   59         this.adminBrokerExecutor =   60             Executors.newFixedThreadPool(this.brokerConfig.getAdminBrokerThreadPoolNums(), new ThreadFactoryImpl(   61                 "AdminBrokerThread_"));   62   63         this.clientManageExecutor = new ThreadPoolExecutor(   64             this.brokerConfig.getClientManageThreadPoolNums(),   65             this.brokerConfig.getClientManageThreadPoolNums(),   66             1000 * 60,   67             TimeUnit.MILLISECONDS,   68             this.clientManagerThreadPoolQueue,   69             new ThreadFactoryImpl("ClientManageThread_"));   70   71         this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor(   72             this.brokerConfig.getHeartbeatThreadPoolNums(),   73             this.brokerConfig.getHeartbeatThreadPoolNums(),   74             1000 * 60,   75             TimeUnit.MILLISECONDS,   76             this.heartbeatThreadPoolQueue,   77             new ThreadFactoryImpl("HeartbeatThread_", true));   78   79         this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor(   80             this.brokerConfig.getEndTransactionThreadPoolNums(),   81             this.brokerConfig.getEndTransactionThreadPoolNums(),   82             1000 * 60,   83             TimeUnit.MILLISECONDS,   84             this.endTransactionThreadPoolQueue,   85             new ThreadFactoryImpl("EndTransactionThread_"));   86   87         this.consumerManageExecutor =   88             Executors.newFixedThreadPool(this.brokerConfig.getConsumerManageThreadPoolNums(), new ThreadFactoryImpl(   89                 "ConsumerManageThread_"));   90   91         this.registerProcessor();   92   93         final long initialDelay = UtilAll.computNextMorningTimeMillis() - System.currentTimeMillis();   94         final long period = 1000 * 60 * 60 * 24;   95         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {   96             @Override   97             public void run() {   98                 try {   99                     BrokerController.this.getBrokerStats().record();  100                 } catch (Throwable e) {  101                     log.error("schedule record error.", e);  102                 }  103             }  104         }, initialDelay, period, TimeUnit.MILLISECONDS);  105  106         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  107             @Override  108             public void run() {  109                 try {  110                     BrokerController.this.consumerOffsetManager.persist();  111                 } catch (Throwable e) {  112                     log.error("schedule persist consumerOffset error.", e);  113                 }  114             }  115         }, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);  116  117         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  118             @Override  119             public void run() {  120                 try {  121                     BrokerController.this.consumerFilterManager.persist();  122                 } catch (Throwable e) {  123                     log.error("schedule persist consumer filter error.", e);  124                 }  125             }  126         }, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);  127  128         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  129             @Override  130             public void run() {  131                 try {  132                     BrokerController.this.protectBroker();  133                 } catch (Throwable e) {  134                     log.error("protectBroker error.", e);  135                 }  136             }  137         }, 3, 3, TimeUnit.MINUTES);  138  139         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  140             @Override  141             public void run() {  142                 try {  143                     BrokerController.this.printWaterMark();  144                 } catch (Throwable e) {  145                     log.error("printWaterMark error.", e);  146                 }  147             }  148         }, 10, 1, TimeUnit.SECONDS);  149  150         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  151  152             @Override  153             public void run() {  154                 try {  155                     log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());  156                 } catch (Throwable e) {  157                     log.error("schedule dispatchBehindBytes error.", e);  158                 }  159             }  160         }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);  161  162         if (this.brokerConfig.getNamesrvAddr() != null) {  163             this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());  164             log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());  165         } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {  166             this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  167  168                 @Override  169                 public void run() {  170                     try {  171                         BrokerController.this.brokerOuterAPI.fetchNameServerAddr();  172                     } catch (Throwable e) {  173                         log.error("ScheduledTask fetchNameServerAddr exception", e);  174                     }  175                 }  176             }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);  177         }  178  179         if (!messageStoreConfig.isEnableDLegerCommitLog()) {  180             if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {  181                 if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {  182                     this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());  183                     this.updateMasterHAServerAddrPeriodically = false;  184                 } else {  185                     this.updateMasterHAServerAddrPeriodically = true;  186                 }  187             } else {  188                 this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  189                     @Override  190                     public void run() {  191                         try {  192                             BrokerController.this.printMasterAndSlaveDiff();  193                         } catch (Throwable e) {  194                             log.error("schedule printMasterAndSlaveDiff error.", e);  195                         }  196                     }  197                 }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);  198             }  199         }  200  201         if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {  202             // Register a listener to reload SslContext  203             try {  204                 fileWatchService = new FileWatchService(  205                     new String[] {  206                         TlsSystemConfig.tlsServerCertPath,  207                         TlsSystemConfig.tlsServerKeyPath,  208                         TlsSystemConfig.tlsServerTrustCertPath  209                     },  210                     new FileWatchService.Listener() {  211                         boolean certChanged, keyChanged = false;  212  213                         @Override  214                         public void onChanged(String path) {  215                             if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {  216                                 log.info("The trust certificate changed, reload the ssl context");  217                                 reloadServerSslContext();  218                             }  219                             if (path.equals(TlsSystemConfig.tlsServerCertPath)) {  220                                 certChanged = true;  221                             }  222                             if (path.equals(TlsSystemConfig.tlsServerKeyPath)) {  223                                 keyChanged = true;  224                             }  225                             if (certChanged && keyChanged) {  226                                 log.info("The certificate and private key changed, reload the ssl context");  227                                 certChanged = keyChanged = false;  228                                 reloadServerSslContext();  229                             }  230                         }  231  232                         private void reloadServerSslContext() {  233                             ((NettyRemotingServer) remotingServer).loadSslContext();  234                             ((NettyRemotingServer) fastRemotingServer).loadSslContext();  235                         }  236                     });  237             } catch (Exception e) {  238                 log.warn("FileWatchService created error, can't load the certificate dynamically");  239             }  240         }  241         initialTransaction();  242         initialAcl();  243         initialRpcHooks();  244     }  245     return result;  246 }

首先完成对…/store/config/topics.json、…/store/config/consumerOffset.json、
…/store/config/subscriptionGroup.json、…/store/config/consumerFilter.json这几个文件的加载

接着创建一个DefaultMessageStore,这是Broker的核心存储

DefaultMessageStore的构造:

 1 private final ConcurrentMap<String/* topic */, ConcurrentMap<Integer/* queueId */, ConsumeQueue>> consumeQueueTable;   2 ......   3 public DefaultMessageStore(final MessageStoreConfig messageStoreConfig, final BrokerStatsManager brokerStatsManager,   4         final MessageArrivingListener messageArrivingListener, final BrokerConfig brokerConfig) throws IOException {   5     this.messageArrivingListener = messageArrivingListener;   6     this.brokerConfig = brokerConfig;   7     this.messageStoreConfig = messageStoreConfig;   8     this.brokerStatsManager = brokerStatsManager;   9     // 请求定位服务  10     this.allocateMappedFileService = new AllocateMappedFileService(this);  11     // 存储服务  12     if (messageStoreConfig.isEnableDLegerCommitLog()) {  13         this.commitLog = new DLedgerCommitLog(this);  14     } else {  15         this.commitLog = new CommitLog(this);  16     }  17     // 消费队列信息  18     this.consumeQueueTable = new ConcurrentHashMap<>(32);  19     // 刷新队列服务  20     this.flushConsumeQueueService = new FlushConsumeQueueService();  21     // 清除CommitLog数据服务  22     this.cleanCommitLogService = new CleanCommitLogService();  23     // 清除消费队列服务  24     this.cleanConsumeQueueService = new CleanConsumeQueueService();  25     this.storeStatsService = new StoreStatsService();  26     // 索引服务  27     this.indexService = new IndexService(this);  28  29     // HA服务,主从复制  30     if (!messageStoreConfig.isEnableDLegerCommitLog()) {  31         this.haService = new HAService(this);  32     } else {  33         this.haService = null;  34     }  35     this.reputMessageService = new ReputMessageService();  36     this.scheduleMessageService = new ScheduleMessageService(this);  37  38     this.transientStorePool = new TransientStorePool(messageStoreConfig);  39  40     if (messageStoreConfig.isTransientStorePoolEnable()) {  41         this.transientStorePool.init();  42     }  43  44     this.allocateMappedFileService.start();  45  46     this.indexService.start();  47  48     this.dispatcherList = new LinkedList<>();  49     this.dispatcherList.addLast(new CommitLogDispatcherBuildConsumeQueue());  50     this.dispatcherList.addLast(new CommitLogDispatcherBuildIndex());  51  52     File file = new File(StorePathConfigHelper.getLockFile(messageStoreConfig.getStorePathRootDir()));  53     MappedFile.ensureDirOK(file.getParent());  54     lockFile = new RandomAccessFile(file, "rw");  55 }

可以看到DefaultMessageStore的构造会创建很多服务,来管理store的存储

其中isEnableDLegerCommitLog用来判断是否使用DLeger,默认false是关闭的
所以在默认情况下使用CommitLog + HAService

 

关于DLeger可参考这篇博客 【Dledger-RocketMQ 基于Raft协议的commitlog存储库】

后续的主从复制还是以CommitLog + HAService为主

回到initialize方法,接着会调用DefaultMessageStore的load方法:

 1 public boolean load() {   2     boolean result = true;   3   4     try {   5         boolean lastExitOK = !this.isTempFileExist();   6         log.info("last shutdown {}", lastExitOK ? "normally" : "abnormally");   7   8         if (null != scheduleMessageService) {   9             result = result && this.scheduleMessageService.load();  10         }  11  12         // load Commit Log  13         result = result && this.commitLog.load();  14  15         // load Consume Queue  16         result = result && this.loadConsumeQueue();  17  18         if (result) {  19             this.storeCheckpoint =  20                 new StoreCheckpoint(StorePathConfigHelper.getStoreCheckpoint(this.messageStoreConfig.getStorePathRootDir()));  21  22             this.indexService.load(lastExitOK);  23  24             this.recover(lastExitOK);  25  26             log.info("load over, and the max phy offset = {}", this.getMaxPhyOffset());  27         }  28     } catch (Exception e) {  29         log.error("load exception", e);  30         result = false;  31     }  32  33     if (!result) {  34         this.allocateMappedFileService.shutdown();  35     }  36  37     return result;  38 }

这里会加载CommitLog和ConsumeQueue对应的文件

接着创建熟悉的NettyRemotingServer,在前面博客中介绍过了,就不再展开
这里会根据nettyServerConfig克隆一份服务端配置,以此创建fastRemotingServer服务端,只不过这个服务端侦听的是上面服务端端口减2的端口号

看过我前面的博客就会发现这个fastRemotingServer的端口号其实就是之前提到过的VIP通道
详见:

【RocketMQ中Producer的启动源码分析】

【RocketMQ中Producer消息的发送源码分析】

接着会根据不同的需求创建很多不同的线程池

然后调用registerProcessor方法:

registerProcessor方法:

 1 public void registerProcessor() {   2    /**   3      * SendMessageProcessor   4      */   5     SendMessageProcessor sendProcessor = new SendMessageProcessor(this);   6     sendProcessor.registerSendMessageHook(sendMessageHookList);   7     sendProcessor.registerConsumeMessageHook(consumeMessageHookList);   8   9     this.remotingServer.registerProcessor(RequestCode.SEND_MESSAGE, sendProcessor, this.sendMessageExecutor);  10     this.remotingServer.registerProcessor(RequestCode.SEND_MESSAGE_V2, sendProcessor, this.sendMessageExecutor);  11     this.remotingServer.registerProcessor(RequestCode.SEND_BATCH_MESSAGE, sendProcessor, this.sendMessageExecutor);  12     this.remotingServer.registerProcessor(RequestCode.CONSUMER_SEND_MSG_BACK, sendProcessor, this.sendMessageExecutor);  13     this.fastRemotingServer.registerProcessor(RequestCode.SEND_MESSAGE, sendProcessor, this.sendMessageExecutor);  14     this.fastRemotingServer.registerProcessor(RequestCode.SEND_MESSAGE_V2, sendProcessor, this.sendMessageExecutor);  15     this.fastRemotingServer.registerProcessor(RequestCode.SEND_BATCH_MESSAGE, sendProcessor, this.sendMessageExecutor);  16     this.fastRemotingServer.registerProcessor(RequestCode.CONSUMER_SEND_MSG_BACK, sendProcessor, this.sendMessageExecutor);  17     /**  18      * PullMessageProcessor  19      */  20     this.remotingServer.registerProcessor(RequestCode.PULL_MESSAGE, this.pullMessageProcessor, this.pullMessageExecutor);  21     this.pullMessageProcessor.registerConsumeMessageHook(consumeMessageHookList);  22  23     /**  24      * QueryMessageProcessor  25      */  26     NettyRequestProcessor queryProcessor = new QueryMessageProcessor(this);  27     this.remotingServer.registerProcessor(RequestCode.QUERY_MESSAGE, queryProcessor, this.queryMessageExecutor);  28     this.remotingServer.registerProcessor(RequestCode.VIEW_MESSAGE_BY_ID, queryProcessor, this.queryMessageExecutor);  29  30     this.fastRemotingServer.registerProcessor(RequestCode.QUERY_MESSAGE, queryProcessor, this.queryMessageExecutor);  31     this.fastRemotingServer.registerProcessor(RequestCode.VIEW_MESSAGE_BY_ID, queryProcessor, this.queryMessageExecutor);  32  33     /**  34      * ClientManageProcessor  35      */  36     ClientManageProcessor clientProcessor = new ClientManageProcessor(this);  37     this.remotingServer.registerProcessor(RequestCode.HEART_BEAT, clientProcessor, this.heartbeatExecutor);  38     this.remotingServer.registerProcessor(RequestCode.UNREGISTER_CLIENT, clientProcessor, this.clientManageExecutor);  39     this.remotingServer.registerProcessor(RequestCode.CHECK_CLIENT_CONFIG, clientProcessor, this.clientManageExecutor);  40  41     this.fastRemotingServer.registerProcessor(RequestCode.HEART_BEAT, clientProcessor, this.heartbeatExecutor);  42     this.fastRemotingServer.registerProcessor(RequestCode.UNREGISTER_CLIENT, clientProcessor, this.clientManageExecutor);  43     this.fastRemotingServer.registerProcessor(RequestCode.CHECK_CLIENT_CONFIG, clientProcessor, this.clientManageExecutor);  44  45     /**  46      * ConsumerManageProcessor  47      */  48     ConsumerManageProcessor consumerManageProcessor = new ConsumerManageProcessor(this);  49     this.remotingServer.registerProcessor(RequestCode.GET_CONSUMER_LIST_BY_GROUP, consumerManageProcessor, this.consumerManageExecutor);  50     this.remotingServer.registerProcessor(RequestCode.UPDATE_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);  51     this.remotingServer.registerProcessor(RequestCode.QUERY_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);  52  53     this.fastRemotingServer.registerProcessor(RequestCode.GET_CONSUMER_LIST_BY_GROUP, consumerManageProcessor, this.consumerManageExecutor);  54     this.fastRemotingServer.registerProcessor(RequestCode.UPDATE_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);  55     this.fastRemotingServer.registerProcessor(RequestCode.QUERY_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);  56  57     /**  58      * EndTransactionProcessor  59      */  60     this.remotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.endTransactionExecutor);  61     this.fastRemotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.endTransactionExecutor);  62  63     /**  64      * Default  65      */  66     AdminBrokerProcessor adminProcessor = new AdminBrokerProcessor(this);  67     this.remotingServer.registerDefaultProcessor(adminProcessor, this.adminBrokerExecutor);  68     this.fastRemotingServer.registerDefaultProcessor(adminProcessor, this.adminBrokerExecutor);  69 }

这里会创建好几种Processor,通过registerProcessor方法同时注册到remotingServer和fastRemotingServer中

registerProcessor方法:

1 public void registerProcessor(int requestCode, NettyRequestProcessor processor, ExecutorService executor) {  2         ExecutorService executorThis = executor;  3     if (null == executor) {  4         executorThis = this.publicExecutor;  5     }  6  7     Pair<NettyRequestProcessor, ExecutorService> pair = new Pair<NettyRequestProcessor, ExecutorService>(processor, executorThis);  8     this.processorTable.put(requestCode, pair);  9 }

这里实际上就是向processorTable进行了记录的添加,为的是后续收到请求能做出对应的处理

processorTable:

1 protected final HashMap<Integer/* request code */, Pair<NettyRequestProcessor, ExecutorService>> processorTable =  2         new HashMap<Integer, Pair<NettyRequestProcessor, ExecutorService>>(64);

 

这里的SendMessageProcessor很重要,后续会详细介绍

 

在完成registerProcessor后,会创建8个定时任务

1 BrokerController.this.getBrokerStats().record();

定时打印Broker的状态

1 BrokerController.this.consumerOffsetManager.persist();

定时向consumerOffset.json文件中写入消费者偏移量

1 BrokerController.this.consumerFilterManager.persist();

定时向consumerFilter.json文件写入消费者过滤器信息

1 BrokerController.this.protectBroker();

定时禁用消费慢的consumer,保护Broker,需要设置disableConsumeIfConsumerReadSlowly属性,默认false

1 BrokerController.this.printWaterMark();

定时打印Send、Pull、Query、Transaction队列信息

1 public void run() {  2     try {  3         log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());  4     } catch (Throwable e) {  5         log.error("schedule dispatchBehindBytes error.", e);  6     }  7 }

定时打印已存储在提交日志中但尚未调度到消费队列的字节数

 1 if (this.brokerConfig.getNamesrvAddr() != null) {   2     this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());   3     log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());   4 } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {   5     this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {   6   7         @Override   8         public void run() {   9             try {  10                 BrokerController.this.brokerOuterAPI.fetchNameServerAddr();  11             } catch (Throwable e) {  12                 log.error("ScheduledTask fetchNameServerAddr exception", e);  13             }  14         }  15     }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);  16 }

若是设置了NamesrvAddr,需要通过updateNameServerAddressList完成一次NameServer地址的跟新(updateNameServerAddressList在前面博客介绍过了)
若是设置了NamesrvAddr,并且设置了fetchNamesrvAddrByAddressServer属性(默认关闭),需要定时获取更新NameServer地址(fetchNameServerAddr方法在之前博客也介绍过)

 1 if (!messageStoreConfig.isEnableDLegerCommitLog()) {   2     if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {   3         if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {   4             this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());   5             this.updateMasterHAServerAddrPeriodically = false;   6         } else {   7             this.updateMasterHAServerAddrPeriodically = true;   8         }   9     } else {  10         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {  11             @Override  12             public void run() {  13                 try {  14                     BrokerController.this.printMasterAndSlaveDiff();  15                 } catch (Throwable e) {  16                     log.error("schedule printMasterAndSlaveDiff error.", e);  17                 }  18             }  19         }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);  20     }  21 }

在非DLeger模式下
若是SLAVE,则需要检查是否设置了HA的Master地址
若设置了Master地址要通过updateHaMasterAddress方法向更新Master地址
updateHaMasterAddress方法实则是在HAClient中实现的:

1 public void updateMasterAddress(final String newAddr) {  2     String currentAddr = this.masterAddress.get();  3     if (currentAddr == null || !currentAddr.equals(newAddr)) {  4         this.masterAddress.set(newAddr);  5         log.info("update master address, OLD: " + currentAddr + " NEW: " + newAddr);  6     }  7 }

非常简单,只是一个比较替换的操作

若没有设置需要更改updateMasterHAServerAddrPeriodically为true,在后面会有用

若是MASTER,则需要定时打印slave落后的字节数

 

设置完定时任务后,和NameServer中一样设置了对SslContext的监听

接着通过initialTransaction方法,加载事务需要的实例
initialTransaction方法:

 1 private void initialTransaction() {   2     this.transactionalMessageService = ServiceProvider.loadClass(ServiceProvider.TRANSACTION_SERVICE_ID, TransactionalMessageService.class);   3     if (null == this.transactionalMessageService) {   4         this.transactionalMessageService = new TransactionalMessageServiceImpl(new TransactionalMessageBridge(this, this.getMessageStore()));   5         log.warn("Load default transaction message hook service: {}", TransactionalMessageServiceImpl.class.getSimpleName());   6     }   7     this.transactionalMessageCheckListener = ServiceProvider.loadClass(ServiceProvider.TRANSACTION_LISTENER_ID, AbstractTransactionalMessageCheckListener.class);   8     if (null == this.transactionalMessageCheckListener) {   9         this.transactionalMessageCheckListener = new DefaultTransactionalMessageCheckListener();  10         log.warn("Load default discard message hook service: {}", DefaultTransactionalMessageCheckListener.class.getSimpleName());  11     }  12     this.transactionalMessageCheckListener.setBrokerController(this);  13     this.transactionalMessageCheckService = new TransactionalMessageCheckService(this);  14 }

这里动态加载了TransactionalMessageService和AbstractTransactionalMessageCheckListener的实现类,位于如下
“META-INF/service/org.apache.rocketmq.broker.transaction.TransactionalMessageService”
“META-INF/service/org.apache.rocketmq.broker.transaction.AbstractTransactionalMessageCheckListener”
还创建了TransactionalMessageCheckService

initialAcl会创建ACL权限检查:

 1 private void initialAcl() {   2    if (!this.brokerConfig.isAclEnable()) {   3         log.info("The broker dose not enable acl");   4         return;   5     }   6   7     List<AccessValidator> accessValidators = ServiceProvider.load(ServiceProvider.ACL_VALIDATOR_ID, AccessValidator.class);   8     if (accessValidators == null || accessValidators.isEmpty()) {   9         log.info("The broker dose not load the AccessValidator");  10         return;  11     }  12  13     for (AccessValidator accessValidator: accessValidators) {  14         final AccessValidator validator = accessValidator;  15         this.registerServerRPCHook(new RPCHook() {  16  17             @Override  18             public void doBeforeRequest(String remoteAddr, RemotingCommand request) {  19                 //Do not catch the exception  20                 validator.validate(validator.parse(request, remoteAddr));  21             }  22  23             @Override  24             public void doAfterResponse(String remoteAddr, RemotingCommand request, RemotingCommand response) {  25             }  26         });  27     }  28 }

需要设置aclEnable属性,默认关闭
若是设置了,同样会加载”META-INF/service/org.apache.rocketmq.acl.AccessValidator”配置的AccessValidator实体类
然后将其包装成RPC钩子,注册到remotingServer和fastRemotingServer中,用于请求的调用validate方法进行ACL权限检查

initialRpcHooks方法则会注册配置了的RPC钩子:

1 private void initialRpcHooks() {  2     List<RPCHook> rpcHooks = ServiceProvider.load(ServiceProvider.RPC_HOOK_ID, RPCHook.class);  3     if (rpcHooks == null || rpcHooks.isEmpty()) {  4         return;  5     }  6     for (RPCHook rpcHook: rpcHooks) {  7         this.registerServerRPCHook(rpcHook);  8     }  9 }

加载”META-INF/service/org.apache.rocketmq.remoting.RPCHook”下的配置的实体类

 

到此Broker启动前的准备工作已经完成,后面start方法会进行真正的启动,在下一篇博客继续分析