目录
一、引言
在网络编程中,我们经常听到各种各样的名词:阻塞、非阻塞、异步、同步。这些,都是 IO 模式的特征。本篇说明了 IO 三种模式,以及简单提及了 NIO 的三大核心组件。
二、IO 模式的特征
1、阻塞与非阻塞
- 阻塞:数据没有传过来时,读会一直阻塞直到有数据传过来;当缓冲区被写满时,写操作也被阻塞。
- 非阻塞:非阻塞遇到上述情况时,直接返回,不会等待。
2、同步与异步
- 同步:数据就绪后需要自己读。
- 异步:数据就绪后直接读好再回调给程序。
三、IO 三种模式
名称 | 模式 | 备注 |
---|---|---|
BIO | 同步阻塞 | Blocking IO,始于 JDK 1.4 之前 |
NIO | 同步非阻塞 | New IO 或 Non Blocking IO,始于 JDK 1.4 |
AIO | 异步 | Async IO |
四、Netty 对三种 IO 模式的支持
NIO | ||||
---|---|---|---|---|
Common | Linux | MacOS/BSD | ||
ThreadPerChannelEventLoopGroup | NioEventLoopGroup | EpollEventLoopGroup | KQueueEventLoopGroup | AioEventLoopGroup |
ThreadPerChannelEventLoop | NioEventLoop | EpollEventLoop | KQueueEventLoop | AioEventLoop |
OioServerSocketChannel | NioServerSocketChannel | EpollServerSocketChannel | KQueueServerSocketChannel | AioServerSocketChannel |
OioSocketChannel | NioSocketChannel | EpollSocketChannel | KQueueSocketChannel | AioSocketChannel |
由上面的表格可以看到 Netty 对于三种 IO 都是支持的,严谨的说是曾经支持过。
1、为什么 Netty 仅支持 NIO 了?
-
BIO 在 Netty 中称 OIO,即 old IO 。我们知道在连接数多的情况下,阻塞意味着性能低,耗资源,所以 OIO 在 Netty 中已经不推荐使用了。
-
AIO 其实在 Netty 5 版本中已经做好支持了,后来在官网发布后一段时间又删除了。究其原因如下:
- Windows 系统的 AIO 实现已经非常成熟了,但是很少用来做服务器。
- Linux 常用来做服务器,但是 AIO 实现不够成熟。
- Linux 系统下 AIO 相比较 NIO 性能提示不明显。
- 更多的维护成本。
2、为什么 Netty 有多种 NIO 实现?
NIO 有一个 Common 实现,针对不同的平台又有其相应的实现。通用的 NIO 实现在 Linux 下也是使用 epoll ,那么既然有了 Common 实现,为什么还要做不同的平台实现呢?
- JDK 在不同的平台有不同的版本,Netty 如此设计也是基于同样的考虑,相比之下 Netty 暴露了更多的可控参数,例如 JDK 的 NIO 实现默认是水平触发,Netty 默认边缘触发并和水平触发可切换。
- Netty 实现垃圾回收更少,性能更好
❤ tips:关于 epoll 的水平触发和边缘触发。
在 Linux 的网络编程中,内核使用 epoll 机制做事件触发,其中 epoll_wait 函数是 epoll 接口的三个函数之一,用于轮询I/O事件的发生。
水平触发通俗来讲:只要有数据,epoll_wait 函数就一直返回;边缘触发通俗来讲:只有 socket 状态发生变化,epoll_wait 函数才会返回。
关于 epoll 的更多,小伙伴若感兴趣可自行谷歌了解。
3、NIO 一定优于 BIO 吗?
相比较 NIO 而言,BIO 的代码实现简单,在特定连接数并发数少的场景下,BIO 的性能不输于 NIO 。
基于以上,学习 Netty 我们必须对 Java 的 NIO 有一定的基础,所以后面一些篇幅是对 Java 的 NIO 知识做一个回顾,即下面讲的 NIO 基于 Java 本身,与 Netty 无关,请小伙伴注意。
五、Java NIO 核心组件
NIO 由三大核心组件组成:
- Channel
- Buffer
- Selector
传统 IO 基于流(Stream)操作,面向字节流或字符流。NIO 与之最大的不同就是基于 Channel 和 Buffer 操作,面向缓冲区。NIO 通过缓冲区可以读取任意位置的数据,相比传统 IO 灵活性大大增强,同时也不会阻塞当前线程。
1、Channel
不同于传统 IO 的流,读写需要使用不同的输入输出流,Channel 是双向的。这意味着它既可以用来进行读操作,又可以用来进行写操作。
NIO中的Channel的主要实现有:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
2、Buffer
Buffer 是缓冲区,本质上是一块内存区域。NIO 中的关键 Buffer 实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。以及 MappedByteBuffer, HeapByteBuffer, DirectByteBuffer 等等。
3、Selector
一个 Selector 可以注册多个 Channel ,Selector 不断地执行查询并判断这些 Channel 是否存在已就绪的 IO 操作,如可读,可写,网络连接已完成等。通过这样的机制,单线程下一个 Selector 就可以管理多个 Channel 了。
六、结语
本篇介绍了三种 IO 模式,以及 Netty 关于三种模式的一些问题讨论,并在结尾简单列举了 NIO 的核心组件,接下来我们将详细的了解 NIO 的核心组件,并学会如何使用它们。