【Netty02】Netty 的I/O模式,什么! Netty 竟然支持过AIO、BIO?

系列文章目录

【Netty01】什么是Netty?



Netty有哪几种 I/O 模式

Netty是一个高性能网络应用框架,它提供了异步的、事件驱动的网络应用程序框架和工具,用于快速开发高性能、高可靠性的网络服务器和客户端程序。在Netty中,主要使用的I/O模式是NIO(同步非阻塞),但历史上Netty也支持过BIO(同步阻塞)和AIO(异步非阻塞)模式。


一、什么是经典的三种 I/O 模式?

特性模式
BIO(Blocking IO)NIO(Non-blocking IO)AIO(Asynchronous IO)
中文意思同步阻塞I/O模式同步非阻塞I/O模式异步非阻塞I/O模式
JDK主要支持时间JDK 1.4 之前JDK 1.4(2002 年,java.nio包)JDK 1.7 (2011 年)
特点在BIO模式下,用户线程在读写时被阻塞,直到数据完全读写完毕。这种模式简单易懂,但效率较低,尤其是在高并发场景下,每个连接都需要一个独立的线程来处理,会造成大量的线程开销和资源浪费。)在NIO模式下,用户线程可以发起一个I/O操作后立即返回,而不会阻塞等待操作完成。这样可以实现一个线程处理多个连接请求,提高了系统的并发处理能力。NIO适用于连接数目多且连接较短的架构,如聊天服务器、弹幕系统等。在AIO模式下,用户线程发起I/O操作后立即返回,不需要等待操作完成。当操作完成时,系统会通知用户线程。这种模式理论上可以实现更高的并发性能,但编程模型相对复杂,且在一些操作系统上支持不够成熟。

二、Netty 历史上对三种 I/O 模式的支持图

在这里插入图片描述


三、为什么 Netty 仅支持 NIO 了?

1.性能考虑
在 Linux 系统上,AIO(Asynchronous I/O)的底层实现仍然使用 EPOLL 模型,与 NIO(Non-blocking I/O)相似,因此在性能上没有明显的优势。另外,AIO 在 Linux 上的实现还不够成熟,处理回调结果的速度可能跟不上处理需求,导致处理速度有瓶颈。而在 Windows 系统上,虽然 AIO 的底层实现良好,但 Netty 的开发人员并没有将 Windows 作为主要使用平台考虑。
2.架构一致性
Netty 的整体架构是基于 Reactor 模型的,而 AIO 是基于 Proactor 模型的。混合使用这两种模型可能会导致架构上的混乱和不一致。
3.资源利用
AIO 在接收数据时需要预先分配缓存,而不是像 NIO 那样在需要接收数据时才分配缓存。这在连接数量非常大且流量较小的情况下可能会浪费内存资源。
4.社区支持和生态系统
随着时间的推移,NIO 成为了 Java 网络编程的主流选择,拥有庞大的社区支持和丰富的生态系统。这使得 Netty 选择支持 NIO 能够更好地利用这些资源,提供稳定且高效的网络编程框架。

四、为什么 Netty 有多种 NIO 实现?

NIO的多种实现(如图所示):
在这里插入图片描述
NIO 的Commn实现在Linux 下也是使用 epoll,为什么还要自己单独实现?
实现的更好!!

  • 自己实现暴露了更多的可控参数,例如:
    • JDK 的NIO默认实现是水平触发
    • Netty 是边缘触发(默认边缘触发)和水平触发可切换
  • Netty 实现的垃圾回收更少、性能更好

五、NIO 一定优于 AIO、BIO 么?

AIO
  • 尽管 AIO 在理论上具有更高的性能潜力,但在实际应用中,其性能表现并不总是优于 NIO。特别是在 Linux 系统上,AIO 的实现存在一些限制和瓶颈,如前文所述,这可能导致其性能不如预期。
  • 此外,AIO 的编程模型相对复杂,使用起来不如 NIO 直观和方便。这也会增加开发者的学习成本和出错的可能性。
  • Netty 的整体架构是基于 Reactor 模型的,而 AIO 是基于 Proactor 模型的。将这两种模型混合在一起可能会增加系统的复杂性,并可能导致性能下降。
BIO
  • BIO 在处理 I/O 操作时,每一个请求都会创建一个新的线程进行处理。当连接数量较大时,这会导致线程数量剧增,进而引发线程切换的开销以及系统资源的过度消耗。因此,BIO 不适用于高并发场景。
  • Netty 作为一个追求高性能的框架,自然更倾向于那些能够减少线程开销、提高系统吞吐量的 I/O 模型。因此,随着 Netty 的发展,BIO 支持被移除或弱化是符合其设计理念的。

六、源码解读 Netty 怎么切换 I/O 模式

1.切换 I/O 模式的源码

代码如下(源码):

/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.example.echo;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.example.util.ServerUtil;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;

/**
 * Echoes back any received data from a client.
 */
public final class EchoServer {

    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx = ServerUtil.buildSslContext();

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(serverHandler);
                 }
             });

            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

在这里插入图片描述


总结

今天我们主要讲了以下内容:

  1. Netty简介与I/O模式:首先介绍了Netty是一个高性能网络应用框架,并提到了它主要使用的I/O模式是NIO(同步非阻塞)。同时也简要提到了历史上Netty也支持过BIO(同步阻塞)和AIO(异步非阻塞)模式。

  2. 经典的三种I/O模式:详细解释了BIO、NIO和AIO三种I/O模式的含义、主要支持时间、特点以及适用场景。其中,BIO简单易懂但效率较低;NIO适用于连接数目多且连接较短的架构;AIO理论上可以实现更高的并发性能,但编程模型复杂且在某些操作系统上支持不够成熟。

  3. Netty对三种I/O模式的支持历史:通过一张图表展示了Netty历史上对BIO、NIO和AIO三种I/O模式的支持情况,指出了Netty最终选择了仅支持NIO。

  4. 为什么Netty仅支持NIO:详细分析了Netty选择仅支持NIO的原因,包括性能考虑、架构一致性、资源利用以及社区支持和生态系统等因素。

  5. Netty中多种NIO实现的原因:解释了Netty为何有多种NIO实现,主要是为了满足不同场景和需求下的性能优化和灵活性。这些实现可能使用了不同的技术或算法,提供了不同的特性和功能,并反映了社区对Netty的积极支持和不断发展的生态系统。

综上,这篇文章主要围绕Netty的I/O模式展开,从经典的三种I/O模式出发,深入探讨了Netty为何选择仅支持NIO以及为何存在多种NIO实现的原因。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值