This paper mainly analyzes the startup process of Producer in RocketMQ.
The version of RocketMQ is: 4.2.0 release.
1, Sequence diagram
According to the source code, the timing of the Producer startup process is drawn once:
2, Source code analysis
1 start(): DefaultMQProducer starts.
The main functions of DefaultMQProducer are implemented in DefaultMQProducer impl. Similarly, most of the functions of DefaultMQPushConsumer are implemented in DefaultMQPushConsumerImpl:
//DefaultMQProducer#start public void start() throws MQClientException { this.defaultMQProducerImpl.start(); }
1.1 checkConfig: check whether the producer group is legal.
// DefaultMQProducerImpl#checkConfig private void checkConfig() throws MQClientException { Validators.checkGroup(this.defaultMQProducer.getProducerGroup()); if (null == this.defaultMQProducer.getProducerGroup()) { throw new MQClientException("producerGroup is null", null); } if (this.defaultMQProducer.getProducerGroup().equals(MixAll.DEFAULT_PRODUCER_GROUP)) {// Cannot be equal to "default UU product" throw new MQClientException("producerGroup can not equal " + MixAll.DEFAULT_PRODUCER_GROUP + ", please specify another one.", null); } } // Validators#checkGroup public static void checkGroup(String group) throws MQClientException { if (UtilAll.isBlank(group)) {// Can not be empty throw new MQClientException("the specified group is blank", null); } if (!regularExpressionMatcher(group, PATTERN)) { throw new MQClientException(String.format( "the specified group[%s] contains illegal characters, allowing only %s", group, VALID_PATTERN_STR), null); } if (group.length() > CHARACTER_MAX_LENGTH) {// Length cannot be greater than 255 throw new MQClientException("the specified group is longer than group max length 255.", null); } }
1.2 getAndCreateMQClientInstance: get MQClientInstance.
//MQClientManager#getAndCreateMQClientInstance public MQClientInstance getAndCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) { String clientId = clientConfig.buildMQClientId();// Build the ClientID of the Producer, equal to the IP address @ instanceName MQClientInstance instance = this.factoryTable.get(clientId); if (null == instance) {// If the current client is not in the mq client instance collection, create an instance and join instance = new MQClientInstance(clientConfig.cloneClientConfig(), this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook); MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance); if (prev != null) {// The application under an IP client can create multiple MQClientInstance objects only when multiple processes are started instance = prev; log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId); } else { log.info("Created new MQClientInstance for clientId:[{}]", clientId); } } return instance; }
1.3 register Producer: register Producer.
// MQClientInstance#registerProducer public boolean registerProducer(final String group, final DefaultMQProducerImpl producer) { if (null == group || null == producer) { return false; } MQProducerInner prev = this.producerTable.putIfAbsent(group, producer);// If not, add the current Producer to the Producer table if (prev != null) { log.warn("the producer group[{}] exist already.", group); return false; } return true; }
1.4 mqclientinstance start mQClientFactory.
// DefaultMQProducerImpl#start(true) if (startFactory) { mQClientFactory.start(); } // MQClientInstance#start public void start() throws MQClientException { synchronized (this) { switch (this.serviceState) { case CREATE_JUST: this.serviceState = ServiceState.START_FAILED; // If not specified,looking address from name server if (null == this.clientConfig.getNamesrvAddr()) { this.mQClientAPIImpl.fetchNameServerAddr();// Get nameService address } // Start request-response channel this.mQClientAPIImpl.start();// Object is responsible for underlying message communication and getting nameService address // Start various schedule tasks this.startScheduledTask();// Start various scheduled tasks // Start pull service this.pullMessageService.start();// Start pull message service // Start rebalance service this.rebalanceService.start();// Start load balancing service at the consumer end // Start push service this.defaultMQProducer.getDefaultMQProducerImpl().start(false);// Call DefaultMQProducerImpl.start() again, and notice that the parameter passed is false. At this time, ServiceState or start failed only calls the heartbeat service this.mQClientFactory.sendHeartbeatToAllBrokerWithLock() once log.info("the client factory [{}] start OK", this.clientId); this.serviceState = ServiceState.RUNNING;// Change serviceState to RUNNING startup state break; case RUNNING: break; case SHUTDOWN_ALREADY: break; case START_FAILED: throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null); default: break; } } }
1.4.1 MQClientAPIImpl#start is responsible for the underlying message communication and starts the client object.
// MQClientAPIImpl#start public void start() { this.remotingClient.start();// RemotingClient is the client that RocketMQ encapsulates Netty network communication }
1.4.2 mqclientinstance ා startscheduledtask start various scheduled tasks.
// MQClientInstance#startScheduledTask private void startScheduledTask() { if (null == this.clientConfig.getNamesrvAddr()) { this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { MQClientInstance.this.mQClientAPIImpl.fetchNameServerAddr();// Update NameServer address } catch (Exception e) { log.error("ScheduledTask fetchNameServerAddr exception", e); } } }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS); } this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { MQClientInstance.this.updateTopicRouteInfoFromNameServer();// Update Topic routing information from nameService } catch (Exception e) { log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e); } } }, 10, this.clientConfig.getPollNameServerInterval(), TimeUnit.MILLISECONDS); this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { MQClientInstance.this.cleanOfflineBroker();// Clean up a hung broker MQClientInstance.this.sendHeartbeatToAllBrokerWithLock();// Send heartbeat message to broker } catch (Exception e) { log.error("ScheduledTask sendHeartbeatToAllBroker exception", e); } } }, 1000, this.clientConfig.getHeartbeatBrokerInterval(), TimeUnit.MILLISECONDS); this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { MQClientInstance.this.persistAllConsumerOffset();// Persist consumerOffset and save the consumer's Offset } catch (Exception e) { log.error("ScheduledTask persistAllConsumerOffset exception", e); } } }, 1000 * 10, this.clientConfig.getPersistConsumerOffsetInterval(), TimeUnit.MILLISECONDS); this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { MQClientInstance.this.adjustThreadPool();// Adjust consumption thread pool } catch (Exception e) { log.error("ScheduledTask adjustThreadPool exception", e); } } }, 1, 1, TimeUnit.MINUTES); }
1.5 mqclientinstance ා sendheartbeattoallbrokerwithlock sends heartbeat information to all brokers.
// MQClientInstance#sendHeartbeatToAllBrokerWithLock public void sendHeartbeatToAllBrokerWithLock() { if (this.lockHeartbeat.tryLock()) { try { this.sendHeartbeatToAllBroker();// Send heartbeat message to all brokers in MQClientInstance.brokerAddrTable list this.uploadFilterClassSource();// Send the register? Message? Filter? Class request code to the filter filter server to update the filter class file in the filter server } catch (final Exception e) { log.error("sendHeartbeatToAllBroker exception", e); } finally { this.lockHeartbeat.unlock(); } } else { log.warn("lock heartBeat, but failed."); } }
The above is the start-up process of Producer in RocketMQ. The main parts are analyzed above. If you want to know the detailed code in the start-up process, you can try debugging and analysis from the clone code on Github to the local. Attach address: 4.2.0 release.