乐趣区

关于java:netty系列之在netty中使用native传输协议

简介

对于 IO 来说,除了传统的 block IO, 应用最多的就是 NIO 了,通常咱们在 netty 程序中最罕用到的就是 NIO, 比方 NioEventLoopGroup,NioServerSocketChannel 等。

咱们也晓得在 IO 中有比 NIO 更快的 IO 形式,比方 kqueue 和 epoll,然而这两种形式须要 native 办法的反对,也就是说须要在操作系统层面提供服务。

如果咱们在反对 Kqueue 或者 epoll 的服务器上,netty 是否能够提供对这些优良 IO 的反对呢?

答案是必定的。然而首先 kqueue 和 epoll 须要 JNI 反对,也就是说 JAVA 程序须要调用本地的 native 办法。

native 传输协定的依赖

要想应用 kequeue 和 epoll 这种 native 的传输方式,咱们须要额定增加我的项目的依赖, 如果是 linux 环境,则能够增加如下的 maven 依赖环境:

  <dependencies>
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-transport-native-epoll</artifactId>
      <version>${project.version}</version>
      <classifier>linux-x86_64</classifier>
    </dependency>
    ...
  </dependencies>

其中 version 须要匹配你所应用的 netty 版本号,否则可能呈现调用异样的状况。

classifier 示意的是零碎架构,它的值能够是 linux-x86_64,也能够是 linux-aarch_64.

如果你应用的 mac 零碎,那么能够这样引入:

  <dependencies>
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-transport-native-kqueue</artifactId>
      <version>${project.version}</version>
      <classifier>osx-x86_64</classifier>
    </dependency>
    ...
  </dependencies>

netty 除了独自的个体包之外,还有一个 all in one 的 netty-all 包,如果你应用了这个 all in one 的包,那么不须要额定增加 native 的依赖。

如果 netty 提供的零碎架构并没有你正在应用的,那么你须要手动进行编译,以下是编译所依赖的程序包, 如果是在 RHEL/CentOS/Fedora 零碎中,则应用:

sudo yum install autoconf automake libtool make tar \
                 glibc-devel \
                 libgcc.i686 glibc-devel.i686

如果是在 Debian/Ubuntu 零碎中,则应用:

sudo apt-get install autoconf automake libtool make tar \
                     gcc

如果是在 MacOS/BSD 零碎中,则应用:

brew install autoconf automake libtool

netty 本地传输协定的应用

装置好依赖包之后,咱们就能够在 netty 中应用这些 native 传输协定了。

native 传输协定的应用和 NIO 的应用基本一致,咱们只须要进行上面的替换即可。

如果是在 liunx 零碎中,则进行上面的替换:


    NioEventLoopGroup → EpollEventLoopGroup
    NioEventLoop → EpollEventLoop
    NioServerSocketChannel → EpollServerSocketChannel
    NioSocketChannel → EpollSocketChannel

如果是在 mac 零碎中,则进行上面的替换:


    NioEventLoopGroup → KQueueEventLoopGroup
    NioEventLoop → KQueueEventLoop
    NioServerSocketChannel → KQueueServerSocketChannel
    NioSocketChannel → KQueueSocketChannel

这里还是应用咱们相熟的聊天服务为例,首先看下基于 Kqueue 的 netty 服务器端应该怎么写:

EventLoopGroup bossGroup = new KQueueEventLoopGroup(1);
        EventLoopGroup workerGroup = new KQueueEventLoopGroup();
        try {ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(KQueueServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new NativeChatServerInitializer());

            Channel channel = b.bind(PORT).sync().channel();
            log.info("server channel:{}", channel);
            channel.closeFuture().sync();

和 NIO 一样,在服务器端咱们须要应用 KQueueEventLoopGroup 创立两个 EventLoopGroup,一个是 bossGroup, 一个是 workerGroup。

而后将这两个 group 传入到 ServerBootstrap 中,并且增加 KQueueServerSocketChannel 作为 channel。

其余的内容和 NIO server 的内容是一样的。

接下来咱们看下基于 Kqueue 的 netty 客户端改如何跟 server 端建设连贯:

EventLoopGroup group = new KQueueEventLoopGroup();
        try {Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(KQueueSocketChannel.class)
             .handler(new NativeChatClientInitializer());

            // 建设连贯
            Channel ch = b.connect(HOST, PORT).sync().channel();
            log.info("client channel: {}", ch);

这里应用的是 KQueueEventLoopGroup,并将 KQueueEventLoopGroup 放到 Bootstrap 中,并且为 Bootstrap 提供了和 server 端统一的 KQueueSocketChannel。

而后就是客户端向 channel 中写音讯,这里咱们间接从命令行输出:

// 从命令行输出
            ChannelFuture lastWriteFuture = null;
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            for (;;) {String line = in.readLine();
                if (line == null) {break;}
                // 将从命令行输出的一行字符写到 channel 中
                lastWriteFuture = ch.writeAndFlush(line + "\r\n");
                // 如果输出 '再见',则期待 server 端敞开 channel
                if ("再见".equalsIgnoreCase(line)) {ch.closeFuture().sync();
                    break;
                }
            }

下面代码的意思是将命令行收到的音讯写入到 channel 中,如果输出的是 ’ 再见 ’,则敞开 channel。

为了可能解决字符串,这里用到了三个编码解码器:

        // 增加行分割器
        pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        // 增加 String Decoder 和 String Encoder, 用来进行字符串的转换
        pipeline.addLast(new StringEncoder());
        pipeline.addLast(new StringDecoder());

别离是行分割器,字符编码器和字符解码器。

运行一下看,程序运行没问题,客户端和服务器端能够进行通信。

总结

这里咱们只以 Kqueue 为例介绍了 netty 中 native 传输协定的应用,具体的代码,大家能够参考:

learn-netty4

更多内容请参考 http://www.flydean.com/52-netty-native-transport-md/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

退出移动版