天天看點

netty系列之:請netty再愛UDT一次

目錄

  • 簡介
  • netty對UDT的支援
    • 搭建一個支援UDT的netty服務
    • 異常來襲
    • TypeUDT和KindUDT
    • 建構ChannelFactory
    • SelectorProviderUDT
  • 使用UDT
  • 總結

UDT是一個非常優秀的協定,可以提供在UDP協定基礎上進行高速資料傳輸。但是可惜的是在netty 4.1.7中,UDT傳輸協定已經被标記為Deprecated了!

意味着在後面的netty版本中,你可能再也看不到UDT協定了.

優秀的協定怎麼能夠被埋沒,讓我們揭開UDT的面紗,展示其優秀的特性,讓netty再愛UDT一次吧。

netty對UDT的支援展現在有一個專門的UDT包來處理UDT相關事情:package io.netty.channel.udt。

這個包裡面主要定義了UDT的各種channel、channel配置、UDT消息和提供ChannelFactory和SelectorProvider的工具類NioUdtProvider.

按照netty的标準流程,現在是需要建立一個netty服務的時候了。

netty建立server服務無非就是建立EventLoop、建立ServerBootstrap、綁定EventLoop、指定channel類型就完了,非常的簡單。

唯一不同的就是具體的childHandler,可能根據具體協定的不同使用不同的處理方式。

當然,如果不是NioSocketChannel,那麼對應的ChannelFactory和SelectorProvider也會有所變化。

沒關系,我們先看下如何建立支援UDT的netty服務:

final ThreadFactory acceptFactory = new DefaultThreadFactory("accept");
        final ThreadFactory connectFactory = new DefaultThreadFactory("connect");
        final NioEventLoopGroup acceptGroup = new NioEventLoopGroup(1, acceptFactory, NioUdtProvider.BYTE_PROVIDER);
        final NioEventLoopGroup connectGroup = new NioEventLoopGroup(1, connectFactory, NioUdtProvider.BYTE_PROVIDER);

 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 UDTEchoServerHandler());
                        }
                    });
            // 開啟服務
            final ChannelFuture future = boot.bind(PORT).sync();
           

可以看到,UDT和普通netty socket服務不同的地方在于它的selector和channelFactory都是由NioUdtProvider來提供了。

NioUdtProvider是netty核心包中的内容,他提供了對UDT的有用封裝,我們不需要要懂太多UDT内部的實作,就可以使用UDT協定,是不是很美妙。

如果有小夥伴興沖沖的拿上面這段代碼去嘗試運作,那麼很可惜你會得到異常,異常大概類似下面的情況:

包com.barchart.udt找不到!
           

什麼?直接使用netty包中的類居然會報錯?是可忍孰不可忍!

我們來仔細分析一下,這裡隻有一個新的類就是NioUdtProvider,打開NioUdtProvider的源碼,在import一欄,我們赫然發現居然引用了不屬于netty的包,就是這些包報錯了:

import com.barchart.udt.SocketUDT;
import com.barchart.udt.TypeUDT;
import com.barchart.udt.nio.ChannelUDT;
import com.barchart.udt.nio.KindUDT;
import com.barchart.udt.nio.RendezvousChannelUDT;
import com.barchart.udt.nio.SelectorProviderUDT;
           

雖然很詭異,但是我們要想程式跑起來還是需要找出這些依賴包,經過本人的跋山涉水、翻山越嶺終于功夫不負苦心人,下面的依賴包找到了:

<dependency>
            <groupId>com.barchart.udt</groupId>
            <artifactId>barchart-udt-core</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.barchart.udt</groupId>
            <artifactId>barchart-udt-bundle</artifactId>
            <version>2.3.0</version>
        </dependency>
           

netty核心包居然要依賴與第三方庫,這可能就是netty準備删除對UDT支援的原因吧。

如果你去檢視barchart中類的具體資訊,就會發現這個包的作者有個癖好,喜歡把類後面帶上一個UDT。

當你看到滿屏的類都是以UDT結尾的時候,沒錯,它就是netty UDT依賴的包barchart本包了。

大牛們開發的包我們不能說他不好,隻能說看起來有點累....

barchart包中有兩個比較核心的用來區分UDT type和kind的兩個類,分别叫做TypeUDT和KindUDT.

Type和kind翻譯成中文好像沒太大差別。但是兩者在UDT中還是有很大不同的。

TypeUDT表示的是UDT socket的模式。它有兩個值,分别是stream和datagram:

STREAM(1),
	DATAGRAM(2),
           

表示資料傳輸是以位元組流的形式還是以資料封包消息的格式來進行傳輸。

KindUDT則用來區分是伺服器端還是用戶端,它有三種模式:

ACCEPTOR,
CONNECTOR,
RENDEZVOUS
           

server模式對應的是ACCEPTOR,用來監聽和接收連接配接.它的代表是ServerSocketChannelUDT,通過調用accept()方法傳回一個CONNECTOR.

CONNECTOR模式可以同時在用戶端和伺服器端使用,它的代表類是SocketChannelUDT.

如果是在server端,則是通過調用server端的accept方法生成的。如果是在用戶端,則表示的是用戶端和伺服器端之間的連接配接。

還有一種模式是RENDEZVOUS模式。這種模式表示的是連接配接的每一側都有對稱對等的channel。也就是一個雙向的模式,它的代表類是RendezvousChannelUDT。

上面提到的兩種Type和三種Kind都是用來定義channel的,是以如果将其混合,會生成六種不同的channelFactory,分别是:

public static final ChannelFactory<UdtServerChannel> BYTE_ACCEPTOR = new NioUdtProvider<UdtServerChannel>(
            TypeUDT.STREAM, KindUDT.ACCEPTOR);

public static final ChannelFactory<UdtChannel> BYTE_CONNECTOR = new NioUdtProvider<UdtChannel>(
            TypeUDT.STREAM, KindUDT.CONNECTOR);

public static final ChannelFactory<UdtChannel> BYTE_RENDEZVOUS = new NioUdtProvider<UdtChannel>(
            TypeUDT.STREAM, KindUDT.RENDEZVOUS);

public static final ChannelFactory<UdtServerChannel> MESSAGE_ACCEPTOR = new NioUdtProvider<UdtServerChannel>(
            TypeUDT.DATAGRAM, KindUDT.ACCEPTOR);

public static final ChannelFactory<UdtChannel> MESSAGE_CONNECTOR = new NioUdtProvider<UdtChannel>(
            TypeUDT.DATAGRAM, KindUDT.CONNECTOR);

public static final ChannelFactory<UdtChannel> MESSAGE_RENDEZVOUS = new NioUdtProvider<UdtChannel>(
            TypeUDT.DATAGRAM, KindUDT.RENDEZVOUS);

           

這些channelFactory通過調用newChannel()方法來生成新的channel。

但是歸根節點,這些channel最後是調用SelectorProviderUDT的from方法來生成channel的。

SelectorProviderUDT根據TypeUDT的不同有兩種,分别是:

public static final SelectorProviderUDT DATAGRAM = 
	new SelectorProviderUDT(TypeUDT.DATAGRAM);

	public static final SelectorProviderUDT STREAM = 
	new SelectorProviderUDT(TypeUDT.STREAM);
           

可以通過調用他的三個方法來生成對應的channel:

public RendezvousChannelUDT openRendezvousChannel() throws IOException {
		final SocketUDT socketUDT = new SocketUDT(type);
		return new RendezvousChannelUDT(this, socketUDT);
	}

    	public ServerSocketChannelUDT openServerSocketChannel() throws IOException {
		final SocketUDT serverSocketUDT = new SocketUDT(type);
		return new ServerSocketChannelUDT(this, serverSocketUDT);
	}

    	public SocketChannelUDT openSocketChannel() throws IOException {
		final SocketUDT socketUDT = new SocketUDT(type);
		return new SocketChannelUDT(this, socketUDT);
	}
           

搭建好了netty伺服器,剩下就是編寫Handler對資料進行處理了。

這裡我們簡單的将用戶端寫入的資料再回寫。用戶端先建立一個message:

message = Unpooled.buffer(UDTEchoClient.SIZE);
 message.writeBytes("www.flydean.com".getBytes(StandardCharsets.UTF_8));
           

再寫入到server端:

public void channelActive(final ChannelHandlerContext ctx) {
        log.info("channel active " + NioUdtProvider.socketUDT(ctx.channel()).toStringOptions());
        ctx.writeAndFlush(message);
    }
           

伺服器端通過channelRead方法來接收:

public void channelRead(final ChannelHandlerContext ctx, Object msg) {
        ctx.write(msg);
    }
           

以上就是netty中使用UDT的原理和一個簡單的例子。

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

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

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

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