大家好,欢迎来到IT知识分享网。
一、简介
1、基本概念
NIO(Non-blocking I/O,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。
2、网络I/O模型
操作系统目前提供的5种I/O模型:阻塞I/O模型、非阻塞I/O模型、I/O复用模型、信号驱动I/O模型和异步I/O模型
3、操作系统相关概念
3.1 内核空间和用户空间
为了保护操作系统的安全行,linux操作系统将虚拟空间分为内核空间和用户空间,不同的空间,拥有自己的内存地址范围。在32位操作系统中,一般将最高的1G自己划分为内核空间,供内核使用,而将较低的3G字节话费为用户空间,供用户进程使用。
- 内核空间:运行内核程序,可以访问所有的系统资源,包括读写磁盘文件、分配回收内存、从网络接口读写数据等;
- 用户空间:运行用户程序,只能执行简单的计算,必须借助内核空间访问系统资源
3.2 缓存I/O
- 读操作:操作系统检查内核的缓冲区没有需要的数据,如果已经缓存了,那么就直接从缓存中返回;否则从磁盘、网卡等中读取,然后存在操作系统的缓存中
- 写操作:将数据从用户空间复制到内核空间的缓存中。这时对用户程序来说写操作就已经完成,至于什么时候再写到磁盘、网卡等中由操作系统决定,除非显示地调用sync同步命令
假设内核空间无需要的数据,用户进程从磁盘或网络读数据分为两个阶段: - 阶段一:内核程序从磁盘、网卡等取数据到内核空间缓存区
- 阶段二:用户程序从内核空间缓存拷贝数据到用户空间
3.3 阻塞I/O
3.4 非阻塞I/O模型的调用过程如下
从图中可看出:
- 阶段一:用户进程调用recvfrom尝试从内核空间获取数据,内核空间数据为准备好则返回EWOULDBLOCK错误,用户进程处于非阻塞状态,但需不断轮询结果;
- 阶段二:内核空间数据准备好后,操作系统将数据从内核空间拷贝到用户空间,拷贝数据过程中,用户进程处于阻塞状态
3.5 I/O复用模型
从图中可看出:
- 阶段一:用户进程将一个或多个fd/socketfd传递给系统调用函数select/poll/epoll,并阻塞在select操作上,系统监听多个fd/socketfd的数据是否准备就绪,此过程用户进程处于阻塞状态;
- 阶段二:有数据准备就绪,遍历所有可读的fd/socketfd,从内核空间拷贝到用户空间,用户进程处于阻塞状态
3.6 信号驱动I/O模型
从图中看出:
- 阶段一:首先开启套接字接口信号驱动I/O功能,并且通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当有数据准备就绪时,就为该进程生成一个SINIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知应用程序处理数据。此阶段用户处于非阻塞状态;
- 阶段二:用户进程调用recvfrom从内核空间获取数据数据,用户进程处于阻塞状态。
3.7 异步I/O模型
从图中可以看出,应用进程发起aio_read操作后,会立即返回,并让内核在整个操作完成后(包括将数据从内核空间复制到用户空间)通知用户进程,告知用户进程操作已完成。阶段一和阶段二用户进程都处于非阻塞状态。
3.8 总结
4 零拷贝
零拷贝不是没有拷贝,而是减少用户态/内核态的切换次数以及CPU拷贝的次数。零拷贝实现方式有多种
4.1 零拷贝实现的几种方式
mmap + write
sendfie
sendfile+DMA scatter/gather实现的零拷贝
splice
mmap与sendfile的区别
在这个基础上,Rocket MQ在消费消息时,使用了mmap。kafka使用了sendfile
二、JAVA NIO
NIO成为同步非阻塞I/O模型。java1.4中引入NIO框架,对应java.nio包,提供了Channel,Selector、Buffer等抽象。
2.1 核心组件
缓冲区:Buffer
通道:Channel
Channel 代表和某一实体的连接,这个实体可以是文件、网络套接字等,java中类似流,但是又有一些区别,通道可以同时进行读写,而流只能读或者写,也可以实现异步读写,也可以从缓存区读数据,写入到缓存区数据。
Channel 相对于传统 IO 的流,有以下特点:
(1)既可以从 Channel 中读取数据,又可以写数据到 Channel。但流的读写通常是单向的;
(2)Channel 可以非阻塞读写,如果数据未准备好,直接返回 0;
(3)Channel 中的数据只能通过 Buffer 进行读写操作。
根据实体的不同,通道可分为:
(1)FileChannel:读写文件;
(2)DatagramChannel: UDP协议网络通信;
(3)SocketChannel:TCP协议网络通信;
选择器:Selector
三、Netty
1、概念
2、特点
- 基于事件机制达成关注点分离(消息编解码、协议编解码、业务处理)
- 可定制的线程处理模型,单线程,多线程池等
- 屏蔽NIO本身的bug
- 性能上的优化
- 相较于NIO接口功能更丰富
- 对外提供统一的接口,底层支持BIO与NIO两种方式自由切换
3、结构
4、Reactor
netty引入了reactor模型,也叫反应器模型,通过注册多个事件和事件回调,当事件发生的时候回主动触发回调完成相应的逻辑。
Reactor模型分为以下几个结构:
1、Reactor:负责事件的监听和分配时间给相应的处理器
2、Handler:与时间绑定,时间发生会回调Handler,进行相应的处理,如IO读写、编解码等;
3、Acceptor:处理客户端连接,并分派请求到处理器链中。
常见的reactor有单线程、多线程、主从多线程三种模式
- Reactor通过多路复用系统调用,收到事件后进行分发
- 连接事件交给Acceptor并创建Handler处理后续的事件
- 非连接事件直接交给handler处理。
- Handler处理完成会返回给客户端
缺点是handler可能会处理一些耗时时间长的任务,导致总体流程阻塞
EventLoopGroup eventGroup = new NioEventLoopGroup(1); ServerBootstrap b = new ServerBootstrap(); b.group(eventGroup)
- Reactor通过多路复用系统调用,收到事件后进行分发
- 连接事件交给Acceptor并创建Handler处理后续的事件
- 非连接事件直接交给Handler处理
- Handler只负责响应时间,不做具体业务处理,通过read读取数据后,会分发给后面的worker线程池进行业务处理。
- Worker线程池会分配独立的线程完成真正的业务处理,将响应的结果发送给handler进行处理
- Handler收到响应结果后会返回给客户端
显而易见相对于单线程,把耗时长的业务逻辑交给线程池来做效率高很多
EventLoopGroup eventGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(eventGroup)
3、主从多线程
- MainReactor负责监听ServerSocket,用来处理网络IO连接事件,将建立的SocketChanner制定注册给SubReactor。
- SubReactor主要和建立连接的Socket做数据交互和业务处理操作。
处理过程:
- 从MainReactor线程池随机选择一个Reactor线程作为Acceptor,用于绑定监听端口,接收客户端连接。MainReactor只负责连接。
- 连接后,从Mainreactor上摘除,重新注册到SubReactor伤感,并创建一个Handler用于处理各种连接事件。
- 当有新的事件发生时,SubReactor会调用连接对应的Handler进行响应。
//主从多线程 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); //第一个线程组就负责连接,第二个线程组负责连接后的线程组里 b.group(bossGroup , workerGroup)
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/138004.html