编写目的:Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。
是对jdk中的IO进行了很多封装与优化,要想全盘理解基于JDK构建出来的Netty,是有必要回忆下jdk中IO的相关知识;
在thing in java I/O章节中,是以 " 对程序语言的设计者来说,创建一个好的输入/输出(I/O)系统是一项艰难的任务" 这样一句话进行了开头;
一:定义
java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。通过流的形式允许java程序使用相同的方式来访问不同的输入/输出源。
二:I/O分类
I/O模型可以从两个维度去理解。
- 阻塞与非阻塞:用户进程发起I/O操作后,根据是否需要等待I/O操作完成才能继续运行可以分为阻塞以及非阻塞。
阻塞与非阻塞针对的是I/O操作的发起者用户进程。
- 同步与异步:当用户进程发起I/O操作后:
- 1:将会由用户态转变为内核态。
- 2:内核进程在接收到I/O操作,完成数据拷贝后,进行I/O操作。
- 3:操作执行完成后,需要从内核态转变为用户态并且进行数据拷贝。
- 4:如果是同步I/O操作,则将数据拷贝到用户空间的过程中用户线程将会阻塞。
- 如果是异步I/O操作,内核线程直接执行数据拷贝的过程,完成拷贝后才通知用户进程I/O操作完成,不会造成用户线程在数据拷贝的时候阻塞。
(ps:什么是用户态与核心态)
三:NIO/BIO 对比
BIO:
传统I/O面向于‘流’ 并存在阻塞,也是B(Locking) I/O 的定义。
意味着当一个线程掉用read()或者write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了,Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。
NIO:
Java的NIO就是上面所说的非阻塞I/O,并且支持多路复用技术,也就是非阻塞多路复用I/O
引入了三位不同于BIO的角色,其定义依次如下:
- Channel(通道):Channel是一个双向的数据读/写通道,Channel可以实现读和写同时操作,并且同时支持阻塞和非阻塞模式,一个客户端连接对应一个Channel,当有新的连接请求时,会向Selector中注册一个Channel,并且会将Channel与对应的事件处理器进行绑定,事件处理器包含了该通道里面的各类事件对应的处理逻辑。
- Buffer(缓存):Channel可以从Buffer中进行读写操作。所有数据的读写都经过缓存区。在NIO中,有两种不同的缓冲区,分别是直接缓冲区和非直接缓冲区。直接缓冲区可以直接操作JVM的堆外内存,即系统内核缓存中分配的缓冲区;非直接缓冲区则只能操作JVM的堆中内存。
- Selector(选择器):Selector通过不断轮询注册在其上的Channel来选择并分发已处理就绪的事件。它可以同时轮询监控多个Channel,当Selector发现某个Channel的数据状态有变化时,会通过SelectorKey触发相关事件,并由对此事件感兴趣的事件处理器来执行相关逻辑,数据则从Buffer中取,执行完后再写会Buffer,以供Channel读取。
引入了Reactor模式
Reactor模式,是一种基于事件驱动的设计模式。Reactor模式的核心思想就是减少线程的等待。当遇到需要等待 IO 时,先释放资源,而在 IO 完成时,再通过事件驱动的方式,继续接下来的处理。从整体上减少了资源的消耗。