BIO/NIO/AIO/NETTY
摘自:芋道源码 :精尽 Netty
同步与阻塞
同步/异步:针对的被调用者的状态。也就是fun函数。(也就是上面的烧水壶)
同步是指fun如果没有结果就不会返回,除非有结果了。
异步是指fun在被调用之后就立即返回了。返回结果之后再通知调用者(可以用信号、回调之类的实现)。
阻塞/非阻塞:主要是针对调用者的,是指程序在等待结果时的状态。(也就是上面的老张在等水开时的状态)
阻塞是指调用线程会被挂起,不做什么别的事情。在得到结果之后才会返回。
非阻塞是指不能立即得到结果,当前线程不会被挂起,还可以做别的事情。
1、BIO
BIO ,全称 Block-IO ,是一种阻塞 + 同步的通信模式。是一个比较传统的通信方式,模式简单,使用方便。但并发处理能力低,通信耗时,依赖网速
原理
- 服务器通过一个 Acceptor 线程,负责监听客户端请求和为每个客户端创建一个新的线程进行链路处理。典型的一请求一应答模式。
- 若客户端数量增多,频繁地创建和销毁线程会给服务器打开很大的压力。后改良为用线程池的方式代替新增线程,被称为伪异步 IO 。
小结
BIO 模型中,通过 Socket 和 ServerSocket 实现套接字通道的通信。阻塞,同步,建立连接耗时。
2、java NIO
NIO ,全称 New IO ,也叫 Non-Block IO ,是一种非阻塞 + 同步的通信模式。
原理
NIO 相对于 BIO 来说一大进步。客户端和服务器之间通过 Channel 通信。NIO 可以在 Channel 进行读写操作。这些 Channel 都会被注册在 Selector 多路复用器上。Selector 通过一个线程不停的轮询这些 Channel 。找出已经准备就绪的 Channel 执行 IO 操作。
NIO 通过一个线程轮询,实现千万个客户端的请求,这就是非阻塞 NIO 的特点。
- 缓冲区 Buffer :它是 NIO 与 BIO 的一个重要区别。
BIO 是将数据直接写入或读取到流 Stream 对象中。
NIO 的数据操作都是在 Buffer 中进行的。Buffer 实际上是一个数组。Buffer 最常见的类型是ByteBuffer,另外还有 CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer。
- 通道 Channel :和流 Stream 不同,通道是双向的。NIO可以通过 Channel 进行数据的读、写和同时读写操作。
通道分为两大类:一类是网络读写(SelectableChannel),一类是用于文件操作(FileChannel)。我们使用的是前者 SocketChannel 和 ServerSocketChannel ,都是SelectableChannel 的子类。
- 多路复用器 Selector :NIO 编程的基础。多路复用器提供选择已经就绪的任务的能力:就是 Selector 会不断地轮询注册在其上的通道(Channel),如果某个通道处于就绪状态,会被 Selector 轮询出来,然后通过 SelectionKey 可以取得就绪的Channel集合,从而进行后续的 IO 操作。
服务器端只要提供一个线程负责 Selector 的轮询,就可以接入成千上万个客户端,这就是 JDK NIO 库的巨大进步。
小结
NIO 模型中通过 SocketChannel 和 ServerSocketChannel 实现套接字通道的通信。非阻塞,同步,避免为每个 TCP 连接创建一个线程。
3、AIO
AIO ,全称 Asynchronous IO ,也叫 NIO2 ,是一种非阻塞 + 异步的通信模式。在 NIO 的基础上,引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。
原理
- AIO 并没有采用 NIO 的多路复用器,而是使用异步通道的概念。其 read,write 方法的返回类型,都是 Future 对象。而 Future 模型是异步的,其核心思想是:去主函数等待时间。
小结
AIO 模型中通过 AsynchronousSocketChannel 和 AsynchronousServerSocketChannel 实现套接字通道的通信。非阻塞,异步。
4、BIO、NIO 区别
-
线程模型不同
- BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。所以,线程开销大。可改良为用线程池的方式代替新创建线程,被称为伪异步 IO
- NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有新的 I/O 请求时,才启动一个线程进行处理。可改良为一个线程处理多个请求,基于 多 Reactor 模型。
-
BIO 是面向流( Stream )的,而 NIO 是面向缓冲区( Buffer )的。
-
BIO 的各种操作是阻塞的,而 NIO 的各种操作是非阻塞的。
-
BIO 的 Socket 是单向的,而 NIO 的 Channel 是双向的。
理解 BIO、NIO、AIO 的流程,可以在理解下图:
----------------------------分割线-----------------------------------
这里说一下IO多路复用、select、poll、epoll、Reactor 模式之间的关系:
IO多路复用的底层实现有 select、poll、epoll,
而 Reactor模式是对IO多路复用的封装,更方便使用,Reactor模式又分为了单Reactor单线程模型,单Reactor多线程模型、主从Reactor模型
一、IO多路复用
即使用一个线程或多个线程处理多个网络连接
- IO指网络IO,需要进行模态转换的读写操作
- 多路,多个客户端连接(socket)
- 复用,复用一个或多个线程
io多路复用是利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效等待,充分利用cpu资源。
在多路复用io模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的i/o读写操作。因为在多路复用i/o模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程。并且只有在真正有socket读写事件时,才会使用io资源,所以它大大减少了资源占用(如cpu)。
多路复用io为何比非阻塞io模型效率高?
因为在非阻塞io中,不断的询问socket状态是通过用户线程去进行的。
而在多路复用io中,轮询每个socket状态是在内核态进行的,所以效率要比用户线程要高的多。
1、常见的io多路复用机制的实现方式有:
-
select:基于轮询的方式去获取就绪连接
-
poll:基于轮询的方式
-
epoll :基于事件驱动的方式去获取就绪连接
二、redis的io多路复用
1、redis线程模型
redis基于Reactor模式设计开发了一套事件处理模型(netty的线程模型也基于Reactor模式)。这套事件处理模型对应的是redis中的文件事件处理器。
2、【io多路复用程序】的实现
3、总结过程