写点什么

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

作者:程序那些事
  • 2022 年 5 月 19 日
  • 本文字数:2711 字

    阅读完需:约 9 分钟

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/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

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

发布于: 2022 年 05 月 19 日阅读数: 32
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:在netty中使用native传输协议_Java_程序那些事_InfoQ写作社区