系列文章目录
【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();
}
}
}
总结
今天我们主要讲了以下内容:
-
Netty简介与I/O模式:首先介绍了Netty是一个高性能网络应用框架,并提到了它主要使用的I/O模式是NIO(同步非阻塞)。同时也简要提到了历史上Netty也支持过BIO(同步阻塞)和AIO(异步非阻塞)模式。
-
经典的三种I/O模式:详细解释了BIO、NIO和AIO三种I/O模式的含义、主要支持时间、特点以及适用场景。其中,BIO简单易懂但效率较低;NIO适用于连接数目多且连接较短的架构;AIO理论上可以实现更高的并发性能,但编程模型复杂且在某些操作系统上支持不够成熟。
-
Netty对三种I/O模式的支持历史:通过一张图表展示了Netty历史上对BIO、NIO和AIO三种I/O模式的支持情况,指出了Netty最终选择了仅支持NIO。
-
为什么Netty仅支持NIO:详细分析了Netty选择仅支持NIO的原因,包括性能考虑、架构一致性、资源利用以及社区支持和生态系统等因素。
-
Netty中多种NIO实现的原因:解释了Netty为何有多种NIO实现,主要是为了满足不同场景和需求下的性能优化和灵活性。这些实现可能使用了不同的技术或算法,提供了不同的特性和功能,并反映了社区对Netty的积极支持和不断发展的生态系统。
综上,这篇文章主要围绕Netty的I/O模式展开,从经典的三种I/O模式出发,深入探讨了Netty为何选择仅支持NIO以及为何存在多种NIO实现的原因。