RocketMQ源碼解析之Producer停止

閱讀須知

  • 文章中使用/* */註釋的方法會做深入分析

正文

Producer 的停止同樣可以依託於 Spring bean 的生命週期,在 Spring bean 銷燬時調用 DefaultMQProducer 的 shutdown 方法:

public void shutdown() {
    /* 停止 Producer */
    this.defaultMQProducerImpl.shutdown();
    if (null != traceDispatcher) {
        traceDispatcher.shutdown();
    }
}

DefaultMQProducerImpl:

public void shutdown() {
    this.shutdown(true);
}

DefaultMQProducerImpl:

public void shutdown(final boolean shutdownFactory) {
    switch (this.serviceState) {
        case CREATE_JUST:
            break;
        case RUNNING:
            /* 註銷 Producer */
            this.mQClientFactory.unregisterProducer(this.defaultMQProducer.getProducerGroup());
            // 停止異步發送線程池
            this.defaultAsyncSenderExecutor.shutdown();
            if (shutdownFactory) {
                /* 停止 MQ 客戶端實例 */
                this.mQClientFactory.shutdown();
            }
            log.info("the producer [{}] shutdown OK", this.defaultMQProducer.getProducerGroup());
            // 將狀態置爲已停止
            this.serviceState = ServiceState.SHUTDOWN_ALREADY;
            break;
        case SHUTDOWN_ALREADY:
            break;
        default:
            break;
    }
}

MQClientInstance:

public void unregisterProducer(final String group) {
    this.producerTable.remove(group);
    /* 從 Broker 註銷 Producer */
    this.unregisterClientWithLock(group, null);
}

MQClientInstance:

private void unregisterClientWithLock(final String producerGroup, final String consumerGroup) {
    try {
        if (this.lockHeartbeat.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
            try {
                /* 註銷 Producer 和 Consumer */
                this.unregisterClient(producerGroup, consumerGroup);
            } catch (Exception e) {
                log.error("unregisterClient exception", e);
            } finally {
                this.lockHeartbeat.unlock();
            }
        } else {
            log.warn("lock heartBeat, but failed.");
        }
    } catch (InterruptedException e) {
        log.warn("unregisterClientWithLock exception", e);
    }
}

這裏我們看到雖然 consumerGroup 傳入的值爲 null,但是 Consumer 停止同樣會複用這個流程。
MQClientInstance:

private void unregisterClient(final String producerGroup, final String consumerGroup) {
    // 遍歷 Broker 地址
    Iterator<Entry<String, HashMap<Long, String>>> it = this.brokerAddrTable.entrySet().iterator();
    while (it.hasNext()) {
        Entry<String, HashMap<Long, String>> entry = it.next();
        String brokerName = entry.getKey();
        HashMap<Long, String> oneTable = entry.getValue();
        if (oneTable != null) {
            for (Map.Entry<Long, String> entry1 : oneTable.entrySet()) {
                String addr = entry1.getValue();
                if (addr != null) {
                    try {
                        /* 從 Broker 註銷 Producer 和 Consumer */
                        this.mQClientAPIImpl.unregisterClient(addr, this.clientId, producerGroup, consumerGroup, 3000);
                        log.info("unregister client[Producer: {} Consumer: {}] from broker[{} {} {}] success", producerGroup, consumerGroup, brokerName, entry1.getKey(), addr);
                    } catch (RemotingException e) {
                        log.error("unregister client exception from broker: " + addr, e);
                    } catch (InterruptedException e) {
                        log.error("unregister client exception from broker: " + addr, e);
                    } catch (MQBrokerException e) {
                        log.error("unregister client exception from broker: " + addr, e);
                    }
                }
            }
        }
    }
}

MQClientAPIImpl:

public void unregisterClient(
    final String addr,
    final String clientID,
    final String producerGroup,
    final String consumerGroup,
    final long timeoutMillis
) throws RemotingException, MQBrokerException, InterruptedException {
    final UnregisterClientRequestHeader requestHeader = new UnregisterClientRequestHeader();
    requestHeader.setClientID(clientID);
    requestHeader.setProducerGroup(producerGroup);
    requestHeader.setConsumerGroup(consumerGroup);
    // 向 Broker 發送 UNREGISTER_CLIENT 命令
    RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UNREGISTER_CLIENT, requestHeader);
    RemotingCommand response = this.remotingClient.invokeSync(addr, request, timeoutMillis);
    assert response != null;
    switch (response.getCode()) {
        case ResponseCode.SUCCESS: {
            return;
        }
        default:
            break;
    }
    throw new MQBrokerException(response.getCode(), response.getRemark());
}

這裏 Broker 對應 UNREGISTER_CLIENT 命令的處理器爲 ClientManageProcessor。從 Broker 註銷 Producer 完成之後,最後就是停止客戶端實例:
MQClientInstance:

public void shutdown() {
    if (!this.consumerTable.isEmpty())
        return;
    if (!this.adminExtTable.isEmpty())
        return;
    if (this.producerTable.size() > 1)
        return;
    synchronized (this) {
        switch (this.serviceState) {
            case CREATE_JUST:
                break;
            case RUNNING:
                this.defaultMQProducer.getDefaultMQProducerImpl().shutdown(false);
                this.serviceState = ServiceState.SHUTDOWN_ALREADY;
                // 停止拉取消息服務
                this.pullMessageService.shutdown(true);
                // 停止定時任務線程池
                this.scheduledExecutorService.shutdown();
                /* 停止遠程調用客戶端 */
                this.mQClientAPIImpl.shutdown();
                // 停止重新平衡服務
                this.rebalanceService.shutdown();
                if (this.datagramSocket != null) {
                    this.datagramSocket.close();
                    this.datagramSocket = null;
                }
                MQClientManager.getInstance().removeClientFactory(this.clientId);
                log.info("the client factory [{}] shutdown OK", this.clientId);
                break;
            case SHUTDOWN_ALREADY:
                break;
            default:
                break;
        }
    }
}

MQClientAPIImpl:

public void shutdown() {
    this.remotingClient.shutdown();
}

NettyRemotingClient:

public void shutdown() {
    try {
        // 取消掃描和終止已棄用請求的定時任務
        this.timer.cancel();
        // 關閉所有遠程調用的 channel
        for (ChannelWrapper cw : this.channelTables.values()) {
            this.closeChannel(null, cw.getChannel());
        }
        this.channelTables.clear();
        // 關閉 netty 內部處理事件的線程組
        this.eventLoopGroupWorker.shutdownGracefully();
        // 關閉處理 netty 用戶自定義事件的線程
        if (this.nettyEventExecutor != null) {
            this.nettyEventExecutor.shutdown();
        }
        // 關閉用於處理請求、響應、鏈接建立、斷開等事件的線程池
        if (this.defaultEventExecutorGroup != null) {
            this.defaultEventExecutorGroup.shutdownGracefully();
        }
    } catch (Exception e) {
        log.error("NettyRemotingClient shutdown exception, ", e);
    }
    // 關閉供處理器處理器請求的線程池
    if (this.publicExecutor != null) {
        try {
            this.publicExecutor.shutdown();
        } catch (Exception e) {
            log.error("NettyRemotingServer shutdown exception, ", e);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章