BookKeeper源碼解析之網絡服務(二)

BookKeeper的網絡服務基於Netty實現,其主要的內容就是構造NettyServer。

構造NettyServer

構造NettyServer之前,初始化eventLoopGroup和監聽端口,然後就是Netty的標準ServerBootstrap的配置流程。

private void listenOn(InetSocketAddress address, BookieSocketAddress bookieAddress) throws InterruptedException {
        if (!conf.isDisableServerSocketBind()) {
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 設置Accept事件循環和work事件循環中的內存分配器
            bootstrap.option(ChannelOption.ALLOCATOR, allocator);
            bootstrap.childOption(ChannelOption.ALLOCATOR, allocator);
          
            // 設置Accept事件循環組和work事件循環組
            bootstrap.group(eventLoopGroup, eventLoopGroup);
            // 是否立即發送
            bootstrap.childOption(ChannelOption.TCP_NODELAY, conf.getServerTcpNoDelay());
            // Timeout to drain the socket on close. 即socket 關閉時可以等待的時間
            bootstrap.childOption(ChannelOption.SO_LINGER, conf.getServerSockLinger());
           
          // 設置自適應的ReceiveBuffer, 真正分配buffer時也是用的上面的allocator
            bootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR,
                    new AdaptiveRecvByteBufAllocator(conf.getRecvByteBufAllocatorSizeMin(),
                            conf.getRecvByteBufAllocatorSizeInitial(), conf.getRecvByteBufAllocatorSizeMax()));
        
          // 高低水位,用於限制客戶端限流
            bootstrap.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(
                    conf.getServerWriteBufferLowWaterMark(), conf.getServerWriteBufferHighWaterMark()));

            if (eventLoopGroup instanceof EpollEventLoopGroup) {
                bootstrap.channel(EpollServerSocketChannel.class);
            } else {
                bootstrap.channel(NioServerSocketChannel.class);
            }
						// 添加channel 添加 hanlder pipeline
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    synchronized (suspensionLock) {
                        while (suspended) {
                            suspensionLock.wait();
                        }
                    }

                    BookieSideConnectionPeerContextHandler contextHandler =
                        new BookieSideConnectionPeerContextHandler();
                    ChannelPipeline pipeline = ch.pipeline();

                    // For ByteBufList, skip the usual LengthFieldPrepender and have the encoder itself to add it
                    // Outbound
                    pipeline.addLast("bytebufList", ByteBufList.ENCODER_WITH_SIZE);
									  // Inbound
                    pipeline.addLast("lengthbaseddecoder", new LengthFieldBasedFrameDecoder(maxFrameSize, 0, 4, 0, 4));
                    // Outbound
                    pipeline.addLast("lengthprepender", new LengthFieldPrepender(4));
                    // Inbound
                    pipeline.addLast("bookieProtoDecoder", new BookieProtoEncoding.RequestDecoder(registry));
                    // Outbound
                    pipeline.addLast("bookieProtoEncoder", new BookieProtoEncoding.ResponseEncoder(registry));
                    // Inbound
                    pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(
                                contextHandler.getConnectionPeer(), authProviderFactory));
										// 正常情況下,channel初始化時,isRunning會被設置成true,並且requestProcessor
                    // 會被設置爲BookieRequestProcessor
                    ChannelInboundHandler requestHandler = isRunning.get()
                            ? new BookieRequestHandler(conf, requestProcessor, allChannels)
                            : new RejectRequestHandler();
                    // Inbound
                    pipeline.addLast("bookieRequestHandler", requestHandler);
										// Inbound
                    pipeline.addLast("contextHandler", contextHandler);
                }
            });

            // Bind and start to accept incoming connections
            Channel listen = bootstrap.bind(address.getAddress(), address.getPort()).sync().channel();
            if (listen.localAddress() instanceof InetSocketAddress) {
                if (conf.getBookiePort() == 0) {
                    conf.setBookiePort(((InetSocketAddress) listen.localAddress()).getPort());
                }
            }
        }

       ...
    }

主要配置是在ChannelInitializer中設置Handler,注意這裏的順序關係,Outbound的handler不能放在最後一個,因爲在調用ctx.writer()時,在最後一個InbounderHandler之後的OutBoundHandler不起作用。

依次添加的InboundHandler

  • LengthFieldBasedFrameDecode:用來解析bytebuf頭部是消息長度類型的消息,配置參數較多,這使用的是(maxFrameSize, 0, 4, 0, 4),其含義如下:

    * BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)
    * +--------+----------------+      +----------------+
    * | Length | Actual Content |----->| Actual Content |
    * | 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
    * +--------+----------------+      +----------------+
    * @param maxFrameLength: frame的最大長度,超過這個會拋出TooLongFrameException
    * @param lengthFieldOffset:長度字段的起始位置
    * @param lengthFieldLength:長度字段的長度
    * @param lengthAdjustment : 長度字段的補償值,表示長度字段內容+lengthAdjustment=實際body的長度
    * @param initialBytesToStrip :需要跳過的bytes數量,即長度字段的長度
      以(maxFrameSize, 0, 4, 0, 4)爲例,這裏是說明長度字段其實位置是0,長度是4個bytes,lengthAdjustment是0 ,說明長度字段表示的就是body字段的長度,initialBytesToStrip表示跳過4bytes字後的數據時body數據
    
  • BookieProtoEncoding.RequestDecoder :BK 協議響應的 decoder,根據協議版本區分處理方式,如果是 v3 版本,則使用 RequestEnDecoderV3 進行 decode 操作;否則使用 RequestEnDeCoderPreV3 執行 decode 操作

  • AuthHandler.ServerSideHandler:認證handler

  • BookieRequestHandler:處理BookkeeperProtocol.Request和 BookieProtocol.Request類型的數據,實際處理類是BookieRequestProcessor,BookieRequestProcessor處理根據請求類型,做不同的處理。

  • BookieSideConnectionPeerContextHandler:保存當前客戶端的相關信息

InboundHanlder的調用順序是正序的,即一條消息的處理會經過:LengthFieldBasedFrameDecode

-> BookieProtoEncoding.RequestDecoder -> AuthHandler.ServerSideHandler -> BookieRequestHandler -> BookieSideConnectionPeerContextHandler。

依次添加的OutboundHandler

  • ByteBufList.ENCODER_WITH_SIZE:一個在message頭部加上四個字節表示message長度的encoder
  • LengthFieldPrepender:一個在message頭部加上長度信息的encoder,這裏指定的長度是4bytes
  • BookieProtoEncoding.ResponseEncoder:BK協議響應的encoder,根據message的類型,使用不同的encoder編碼,BookkeeperProtocol.Response -> ResponseEnDecoderV3, BookieProtocol.Response -> ResponseEnDeCoderPreV3,默認直接發向下一個handler

OUtboundHandler的調用順序是逆序的,即一條消息的處理經過:BookieProtoEncoding.ResponseEncoder ->

LengthFieldPrepender -> ByteBufList.ENCODER_WITH_SIZE

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