RocketMQ source code: Producer startup analysis

Keywords: Netty network github

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.

Posted by pjleonhardt on Wed, 01 Jan 2020 06:57:01 -0800