Netty#1 NIO基本概念

Posted 代码荣耀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty#1 NIO基本概念相关的知识,希望对你有一定的参考价值。

点击上方蓝色小字,关注后端技术
碎片时间x体系学习
这是第153原创;2019年第1
交流微信:friendfb


00


基本概念

 


我们知道,Netty这座大厦是构建在Java NIO之上的,在深入Netty介绍之前,我们需要对Java NIO一些重要概念进行说明,正确理解这些概念是我们掌握Netty的关键。


0 同步、异步、阻塞、非阻塞

一语概之:Unix IO模型的语境下,同步和异步的区别在于数据拷贝阶段是否需要完全由操作系统处理;完全由操作系统处理则为异步,否则为同步。

阻塞和非阻塞操作是针对发起IO请求操作后是否有立刻返回一个标志信息而不让请求线程等待;立即返回一个标志后,应用程序不断循环获取操作是否完成的状态是为非阻塞,否则为同步。以下是常见的四中IO模型。


  • 同步阻塞 IO

用户进程在发起一个 IO 操作以后,必须等待 IO 操作的完成,只有当真正完成了 IO 操作以后,用户进程才能运行。 Java 传统的 IO 模型属于此种方式。


  • 同步非阻塞 IO

用户进程发起一个 IO 操作以后便可返回做其它事情,但是用户进程需要不断询问 IO 操作是否就绪,从而引入不必要的 CPU 资源浪费。其中目前 Java 的 NIO 就属于同步非阻塞 IO 。


  • 异步阻塞 IO

应用发起一个 IO 操作以后,不等待内核 IO 操作的完成,等内核完成 IO 操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问 IO 是否完成;那么为什么说是阻塞的呢?因为此时是通过 select 系统调用来完成的,而 select 函数本身的实现方式是阻塞的,而采用 select 函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性。


  • 异步非阻塞 IO

用户进程只需要发起一个 IO 操作然后立即返回,等 IO 操作真正的完成以后,应用程序会得到 IO 操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的 IO 读写操作,因为真正的 IO 读取或者写入操作已经由内核完成了。


1 Channel

Channel称为通道,是一个用来完成应用程序和操作系统交互事件、传递内容的渠道,注意是连接到操作系统。一个通道会有一个专属的文件状态描述符。既然是和操作系统进行内容的传递,那就说明应用程序可以通过通道从操作系统读取数据,也可以通过通道向操作系统写数据。


所有被Selector(选择器)注册的通道,只能是继承了SelectableChannel类的子类。其中有几个关键的Channel通道实现:

  • ServerSocketChannel:应用服务器端程序的监听通道。只有通过这个通道,应用程序才能向操作系统注册支持“多路复用I/O”的端口监听。可同时支持UDP协议和TCP协议。

  • SocketChannel:TCP Socket套接字的监听通道,一个Socket套接字对应了一个客户端(IP和端口)到服务器端(IP和端口)的通信连接。

  • DatagramChannel:UDP数据报文的监听通道。


2 Buffer

Buffer又称“数据缓冲区”。在Java原始NIO框架中,为了保证每个通道的数据读/写速度,Java NIO框架为每一种需要支持数据读/写的通道集成了Buffer的支持,如Fig.1所示。

Fig.1 Buffer实现类


Buffer有两种工作模式:写模式和读模式。在读模式下,应用程序智能从Buffer中读取数据,不能进行写操作。但是在写模式下,应用程序是可以进行读操作的,这就表示可能会出现脏读。所以一旦决定要从Buffer中读取数据,就一定要将Buffer的状态改为读模式。默认是写模式,使用flip()函数可以切换到读模式。要正确使用Buffer,需要理解Buffer几个重要的属性,如图Fig.2所示。


Netty#1 NIO基本概念

Fig.2 图解Buffer类的重要属性


  • position:缓冲区目前正在操作的数据块位置。写模式下表示开始写入的位置;读模式下表示开始读的位置。

  • limit:缓冲区最大可以进行操作的位置。缓存区的读/写状态正是由这个属性控制的。在写模式下初始的时候等于capacity;在读模式下,等于最后一次写入的位置.

  • capacity:缓冲区的最大容量。这个容量是在缓存区创建时指定的。由于高并发时,通道数量往往会很大,所以每个缓存区的容量最好不要太大。

  • mark——标记位,标记一下position的位置,可以调用reset()方法回到这个位置。


Buffer的常见方法如下所示:

  • flip(): 写模式转换成读模式。

  • rewind():将 position 重置为 0 ,一般用于重复读。

  • clear():清空 buffer ,准备再次被写入 (position 变成 0 , limit 变成 capacity) 。

  • compact(): 将未读取的数据拷贝到 buffer 的头部位。

  • mark()/reset():mark 可以标记一个位置, reset 可以重置到该位置。

  • Buffer 常见类型: ByteBuffer 、 MappedByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 、 ShortBuffer 。


3 Selector

Selector又称为“选择器”。其作用包括:

  • 事件订阅和Channel管理。应用程序将向Selector对象注册需要它关注的Channel,以及具体的某一个Channel会对哪些I/O事件感兴趣。Selector中会维护一个“已经注册的Channel”的容器。

  • 轮询代理。应用层不再通过阻塞模式或非阻塞模式直接询问操作系统“事件有没有发生”,而是由Selector代其询问。

  • 实现不同操作系统的支持。NIO(多路复用I/O技术)是需要操作系统进行支持的,其特点就是操作系统可以同时扫描同一个端口上的多个网络连接。所以作为上层的JVM,必须要为不同操作系统的多路复用I/O实现编写不同的代码实现。


01


Java NIO体系



理解了NIO的核心概念后,我们现在开始进一步深入理解NIO。NIO是基于事件驱动思想的,实现上采用Reactor模式(后续会有文章详细说明,这里知道这是一种设计模式即可)。从程序角度而言,当发起I/O的读或写操作时,是非阻塞的;当Socket有流可读或可写入Socket时,操作系统会相应地通知应用程序进行处理,应用再将流读取到缓冲区或写入操作系统。对于网络I/O而言,主要有接受、连接、读取及写入四种类型事件。


事件名

对应值

服务端接收客户端连接

SelectionKey.OP_ACCEPT

客户端连接服务端

SelectionKey.OP_CONNECT

读事件

SelectionKey.OP_READ

写事件

SelectionKey.OP_WRITE


在Java中可基于java.nio.channels中的Channel、Buffer、Selector的三个核心相关类来实现TCP/IP+NIO方式的系统间通信。


其中,Channel有SocketChannel和ServerSocketChannel两种,SocketChannel用于建立连接、监听事件及操作读写,ServerSocketChannel用于监听端口及监听连接事件。为了便于理解,你可以将Channel和文件或者网络Socket对应。如果Channel对应着一个Socket,那么往这个Channel中写数据,就等同于向Socket中写入数据。


和Channel配合使用的另外一个重要组件就是Buffer。大家可以简单的将Buffer理解成一个内存区域或者byte数组。数据需要包装成Buffer的形式才能和Channel进行读写操作。


另外一个与Channel密切相关的是Selector组件。程序通过Selector来获取是否有要处理的事件。在Channel的众多实现中,有一个SelectableChannel实现,表示可被选择的通道。任何一个SelectableChannel都可以将自己注册到一个Selector重。这样,这个Channel就能被Selector所管理。一个Selector可以管理多个SelectableChannel。当SelectableChannel的数据准备好时,Selector就会接到通知,得到那些已经准备好的数据。Selector与Channel的关系如Fig.3所示。


Netty#1 NIO基本概念

Fig.3 Selector与Channel


为此,一个Selector可以由一个线程进行管理,而一个SocketChannel则可以表示一个客户端连接,因此,这就构成了一个或者极少数线程,来处理大量客户端连接的结构。当与客户端连接的数据还没有准备好时,Selector会处于等待状态。由于用于管理Selector的线程数是很少的,不会造成太大的系统资源浪费;而一旦有任何一个SocketChannel准备好了数据,Selector就能立即得到通知,获取数据进行处理。这就构成了Java NIO体系结构,如图Fig.4所示。

Fig.4 Java NIO体系结构



02


小结

  


综上,本文是Netty精讲的开胃菜;下篇文章讲述Netty的线程模型,敬请期待。



One More Thing


推荐1:

上文1:

上文2:


你负责认真,我们负责帮你解决问题,让改变发生;欢迎大家扫一扫下图的二维码加入知识星球。期待 2019,在程序猿成长的道路上,共同进化!


以上是关于Netty#1 NIO基本概念的主要内容,如果未能解决你的问题,请参考以下文章

即时通讯开发之Netty入门长文:基本介绍环境搭建

浅谈Netty相关概念

Netty—— 概念剖析(零拷贝)

Netty—— 概念剖析(零拷贝)

Java NIO的基本概念ChannelBufferSelector以及非阻塞网络通信案例

彻底搞懂 Netty 线程模型