RocketMq中網絡通信之服務端

 

  • 一,Broker服務端入口(NettyServer端)

首先RocketMq網絡通信採用的Netty通信。服務端主要集中在Broker中。我們先看一下Broker的啓動類BrokerStartup

顯然具體邏輯是在start方法裏面,下面是實現:

 public void start() throws Exception {
        if (this.messageStore != null) {
            this.messageStore.start();
        }

        if (this.remotingServer != null) {
            this.remotingServer.start();
        }

        if (this.fastRemotingServer != null) {
            this.fastRemotingServer.start();
        }

        if (this.fileWatchService != null) {
            this.fileWatchService.start();
        }

        if (this.brokerOuterAPI != null) {
            this.brokerOuterAPI.start();
        }

        if (this.pullRequestHoldService != null) {
            this.pullRequestHoldService.start();
        }

        if (this.clientHousekeepingService != null) {
            this.clientHousekeepingService.start();
        }

        if (this.filterServerManager != null) {
            this.filterServerManager.start();
        }

        if (!messageStoreConfig.isEnableDLegerCommitLog()) {
            startProcessorByHa(messageStoreConfig.getBrokerRole());
            handleSlaveSynchronize(messageStoreConfig.getBrokerRole());
        }



        this.registerBrokerAll(true, false, true);

        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                try {
                    BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
                } catch (Throwable e) {
                    log.error("registerBrokerAll Exception", e);
                }
            }
        }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);

        if (this.brokerStatsManager != null) {
            this.brokerStatsManager.start();
        }

        if (this.brokerFastFailure != null) {
            this.brokerFastFailure.start();
        }


    }

可以從名字大致猜出接收遠程消息是remotingServer.start(),點進去觀察一下其具體實現:

這裏看到我們熟悉的面孔ServerBootStrap, 那麼可以明確一點,我們要知道的具體通信協議實現,必定是寫在一個handler裏面的:

.childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline()
                            .addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME,
                                new HandshakeHandler(TlsSystemConfig.tlsMode))
                            .addLast(defaultEventExecutorGroup,
                                new NettyEncoder(),
                                new NettyDecoder(),
                                new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
                                new NettyConnectManageHandler(),
                                new NettyServerHandler()
                            );
                    }

從這些handler中,由名字可以猜測,通信消息的解析發生在NettyServerHandler,進入NettyServerHandler:

由上圖可知,它本身就是一個讀消息的Handler, 可以看到的是接收的消息體是RemotingCommand。這個類必然就是整個RocketMq的通信協議。

點進去看一下:

大致上看由code、Header、body以及一些metedata組成。其實所有的Rpc調用框架基本上都是這個設計思路。所有的請求必須繼承自某一個父類。

不過現在的微服務體系似乎沒有這樣子做,可能是出於不同的服務需求多樣性考慮,但是沒有統一的請求頭着實怪異,後續有時間看一下這方面的設計考慮。

至此,Rpc的Netty調用鏈基本結束。

  •  二,RocketMq 通信編碼 

由一的部分成功定位到了接收消息的入口,本章着重講解其解析消息的細節實現。

rocketMq通信協議Netty採用的是面向字節流的報文設計。在發送端,前4個字節存儲整個報文長度,緊接着4個字節存儲頭信息,然後緊接着發送body字節流。源碼如下:

 public ByteBuffer encodeHeader(final int bodyLength) {
        // 定義頭4個字節儲存整個報文長度
        int length = 4;

        // 計算頭部長度
        byte[] headerData;
        headerData = this.headerEncode();
        length += headerData.length;

        // 計算body長度
        length += bodyLength;

     // 頭部信息:整體報文長度信息(4個字節) + 頭部數據(length-bodyLehth) ByteBuffer result = ByteBuffer.allocate(4 + length - bodyLength); // 第一個4字節: 存放報文整體長度信息,從這裏我們可以看到meaasge的消息長度是有限制的 result.putInt(length); // 第二個4字節: 第一個字節存放的是序列化類型,有Java或者RocketMq類型。後三個字節存放的是頭部數據長度 result.put(markProtocolType(headerData.length, serializeTypeCurrentRPC)); // 寫入頭部數據 result.put(headerData); result.flip(); return result; }

  通過對編碼部分源碼學習,一般對字節的操作喜歡用位運算符,比如要整型的第三個字節,int >>>0xff & 0xff 即可。下面是rocketMq解析的部分示例代碼:

   public static SerializeType getProtocolType(int source) {
        return SerializeType.valueOf((byte) ((source >> 24) & 0xFF));
    }

  待續。。。。。。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章