【mq】從零開始實現 mq-04-啟動檢測與實現優化

老馬嘯西風發表於2022-05-08

前景回顧

【mq】從零開始實現 mq-01-生產者、消費者啟動

【mq】從零開始實現 mq-02-如何實現生產者呼叫消費者?

【mq】從零開始實現 mq-03-引入 broker 中間人

【mq】從零開始實現 mq-04-啟動檢測與實現優化

上一節我們引入了中間人 broker,讓訊息的生產者和消費者解耦。

這一節我們對初始化程式碼進行優化,便於後期擴充維護。

啟動檢測

生產者啟動優化

啟動實現

整體實現調整如下:

@Override
public synchronized void run() {
    this.paramCheck();
    // 啟動服務端
    log.info("MQ 生產者開始啟動客戶端 GROUP: {}, PORT: {}, brokerAddress: {}",
            groupName, port, brokerAddress);
    try {
        //channel future
        this.channelFutureList = ChannelFutureUtils.initChannelFutureList(brokerAddress,
                initChannelHandler(), check);

        // register to broker
        this.registerToBroker();

        // 標識為可用
        enableFlag = true;
        log.info("MQ 生產者啟動完成");
    } catch (Exception e) {
        log.error("MQ 生產者啟動遇到異常", e);
        throw new MqException(ProducerRespCode.RPC_INIT_FAILED);
    }
}

看起來是不是比起原來清爽很多呢?

但是複雜性只會轉移,不會消失

答案就是封裝到 initChannelFutureList 中去了。

initChannelFutureList

因為這裡是生產者、消費者都會用到。

所以我們先放在統一的工具類中,實現本身和以前大同小異。

/**
 * 初始化列表
 * @param brokerAddress 地址
 * @param channelHandler 處理類
 * @param check 是否檢測可用性
 * @return 結果
 * @since 0.0.4
 */
public static List<RpcChannelFuture> initChannelFutureList(final String brokerAddress,
                                                           final ChannelHandler channelHandler,
                                                           final boolean check) {
    List<RpcAddress> addressList = InnerAddressUtils.initAddressList(brokerAddress);
    List<RpcChannelFuture> list = new ArrayList<>();
    for(RpcAddress rpcAddress : addressList) {
        try {
            final String address = rpcAddress.getAddress();
            final int port = rpcAddress.getPort();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            Bootstrap bootstrap = new Bootstrap();
            ChannelFuture channelFuture = bootstrap.group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<Channel>(){
                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            ch.pipeline()
                                    .addLast(new LoggingHandler(LogLevel.INFO))
                                    .addLast(channelHandler);
                        }
                    })
                    .connect(address, port)
                    .syncUninterruptibly();
            log.info("啟動客戶端完成,監聽 address: {}, port:{}", address, port);
            RpcChannelFuture rpcChannelFuture = new RpcChannelFuture();
            rpcChannelFuture.setChannelFuture(channelFuture);
            rpcChannelFuture.setAddress(address);
            rpcChannelFuture.setPort(port);
            rpcChannelFuture.setWeight(rpcAddress.getWeight());
            list.add(rpcChannelFuture);
        } catch (Exception exception) {
            log.error("註冊到 broker 服務端異常", exception);
            if(check) {
                throw new MqException(MqCommonRespCode.REGISTER_TO_BROKER_FAILED);
            }
        }
    }

    if(check
        && CollectionUtil.isEmpty(list)) {
        log.error("check=true 且可用列表為空,啟動失敗。");
        throw new MqException(MqCommonRespCode.REGISTER_TO_BROKER_FAILED);
    }
    return list;
}

這裡的 check 為了避免 2 種情況:

(1)某一個 broker 不可用

(2)沒有可用的 broker 資訊。

消費者啟動優化

消費者連線 broker 和生產者是類似的。

這裡只是放一下實現,不做更多的贅述。

@Override
public void run() {
    // 啟動服務端
    log.info("MQ 消費者開始啟動服務端 groupName: {}, brokerAddress: {}",
            groupName, brokerAddress);
    //1. 引數校驗
    this.paramCheck();
    try {
        //channel future
        this.channelFutureList = ChannelFutureUtils.initChannelFutureList(brokerAddress,
                initChannelHandler(),
                check);

        // register to broker
        this.registerToBroker();

        // 標識為可用
        enableFlag = true;
        log.info("MQ 消費者啟動完成");
    } catch (Exception e) {
        log.error("MQ 消費者啟動異常", e);
        throw new MqException(ConsumerRespCode.RPC_INIT_FAILED);
    }
}

小結

這一小節的內容特別簡單,對初始化部分做了優化,便於後期維護擴充。

希望本文對你有所幫助,如果喜歡,歡迎點贊收藏轉發一波。

我是老馬,期待與你的下次重逢。

開源地址

The message queue in java.(java 簡易版本 mq 實現) https://github.com/houbb/mq

擴充閱讀

rpc-從零開始實現 rpc https://github.com/houbb/rpc

相關文章