netty系列之:選byte還是選message?這是一個問題

flydean發表於2022-02-10

簡介

UDT給了你兩種選擇,byte stream或者message,到底選哪一種呢?經驗告訴我們,只有小學生才做選擇題,而我們應該全都要!

型別的定義

UDT的兩種型別是怎麼定義的呢?

翻看com.barchart.udt包,可以發現這兩種型別定義在TypeUDT列舉類中。

    STREAM(1),
    DATAGRAM(2), 

一個叫做STREAM,它的code是1。一個叫做DATAGRAM,他的code是2.

根據兩個不同的型別我們可以建立不同的selectorProvider和channelFactory。而這兩個正是構建netty服務所需要的。

在NioUdtProvider這個工具類中,netty為我們提供了TypeUDT和KindUDT的六種組合ChannelFactory,他們分別是:

用於Stream的:BYTE_ACCEPTOR,BYTE_CONNECTOR,BYTE_RENDEZVOUS。

和用於Message的:MESSAGE_ACCEPTOR,MESSAGE_CONNECTOR和MESSAGE_RENDEZVOUS。

同樣的,還有兩個對應的SelectorProvider,分別是:

BYTE_PROVIDER 和 MESSAGE_PROVIDER.

搭建UDT stream伺服器

如果要搭建UDT stream伺服器,首先需要使用NioUdtProvider.BYTE_PROVIDER來建立NioEventLoopGroup:

        final NioEventLoopGroup acceptGroup = new NioEventLoopGroup(1, acceptFactory, NioUdtProvider.BYTE_PROVIDER);
        final NioEventLoopGroup connectGroup = new NioEventLoopGroup(1, connectFactory, NioUdtProvider.BYTE_PROVIDER);

這裡,我們建立兩個eventLoop,分別是acceptLoop和connectLoop。

接下來就是在ServerBootstrap中繫結上面的兩個group,並且指定channelFactory。這裡我們需要NioUdtProvider.BYTE_ACCEPTOR:

final ServerBootstrap boot = new ServerBootstrap();
            boot.group(acceptGroup, connectGroup)
                    .channelFactory(NioUdtProvider.BYTE_ACCEPTOR)
                    .option(ChannelOption.SO_BACKLOG, 10)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<UdtChannel>() {
                        @Override
                        public void initChannel(final UdtChannel ch) {
                            ch.pipeline().addLast(
                                    new LoggingHandler(LogLevel.INFO),
                                    new UDTByteEchoServerHandler());
                        }
                    });

就這麼簡單。

搭建UDT message伺服器

搭建UDT message伺服器的步驟和stream很類似,不同的是需要使用NioUdtProvider.MESSAGE_PROVIDER作為selectorProvider:

        final NioEventLoopGroup acceptGroup =
                new NioEventLoopGroup(1, acceptFactory, NioUdtProvider.MESSAGE_PROVIDER);
        final NioEventLoopGroup connectGroup =
                new NioEventLoopGroup(1, connectFactory, NioUdtProvider.MESSAGE_PROVIDER);

然後在繫結ServerBootstrap的時候使用NioUdtProvider.MESSAGE_ACCEPTOR作為channelFactory:

final ServerBootstrap boot = new ServerBootstrap();
            boot.group(acceptGroup, connectGroup)
                    .channelFactory(NioUdtProvider.MESSAGE_ACCEPTOR)
                    .option(ChannelOption.SO_BACKLOG, 10)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<UdtChannel>() {
                        @Override
                        public void initChannel(final UdtChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(
                                    new LoggingHandler(LogLevel.INFO),
                                    new UDTMsgEchoServerHandler());
                        }
                    });

同樣很簡單。

Stream和Message的handler

不同的UDT型別,需要使用不同的handler。

對於Stream來說,它的底層是byte,所以我們的訊息處理也是以byte的形式進行的,我們以下面的方式來構建message:

private final ByteBuf message;
message = Unpooled.buffer(UDTByteEchoClient.SIZE);
        message.writeBytes("www.flydean.com".getBytes(StandardCharsets.UTF_8));

然後使用ctx.writeAndFlush(message)將其寫入到channel中。

對於message來說,它實際上格式對ByteBuf的封裝。netty中有個對應的類叫做UdtMessage:

public final class UdtMessage extends DefaultByteBufHolder

UdtMessage是一個ByteBufHolder,所以它實際上是一個ByteBuf的封裝。

我們需要將ByteBuf封裝成UdtMessage:

private final UdtMessage message;
final ByteBuf byteBuf = Unpooled.buffer(UDTMsgEchoClient.SIZE);
        byteBuf.writeBytes("www.flydean.com".getBytes(StandardCharsets.UTF_8));
        message = new UdtMessage(byteBuf);

然後將這個UdtMessage傳送到channel中:

ctx.writeAndFlush(message);

這樣你就學會了在UDT協議中使用stream和message兩種資料型別了。

總結

大家可能覺得不同的資料型別原來實現起來這麼簡單。這全都要歸功於netty優秀的封裝和設計。

感謝netty!

本文的例子可以參考:learn-netty4

本文已收錄於 http://www.flydean.com/40-netty-udt-support-2/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!

相關文章