个人博客网:https://wushaopei.github.io/ (你想要这里多有)
NIO 内容概览:
- NIO 网络编程模型
- NIO 网络编程详解
- NIO 网络编程实战
- NIO 网络编程缺陷
一、NIO网络编程模型
1、编程模型定义
NIO : 又叫Non-blocking I/O或New I/O;全新的输入输出标准库;
做为原始I/O的补充,为了高性能和高并发的场景使用。
模型:对事物共性的抽象;
编程模型:对编程共性的抽象;
2、BIO网络模型
- 服务端启动,开始建立监听客户端的连接请求;
- 客户端启动,向服务器端发起建立连接请求;
- 服务器在收到客户端的请求后,将会创建一个新的线程;
- 服务端新创建的线程会与客户端建立socket连接,用于响应客户端的请求,通知客户端连接建立成功,你随时可以给我发送数据。
- 服务器端处理完客户端的请求之后,就会处于等待状态,等待客户端再次发起请求
服务端为每一个客户端建一个线程,一旦客户端请求过多,服务端线程数量也会增多,服务端压力增大。
3、BIO网络模型缺点
-
阻塞式I/O模型,会导致服务器端的业务线程会因阻塞IO的问题一直阻塞等待客户端发起请求,如果客户端不发起请求,服务端的业务线程会一直存在,就会耗费大量系统资源
-
弹性伸缩能力差:服务器端的线程数与客户端的个数呈1比1的关系
-
多线程耗资源 : 每一个线程都会对CPU的调度资源进行占用,一旦占用而不释放,则会导致资源的紧缺、甚至系统服务的异常宕机
4、NIO网络模型猜想
基于非阻塞 I/O , 设计应对高并发场景编程模型
图解分析:NIO架构中,客户端的个数与服务器端的线程数呈M:1的关系
5、NIO 网络模型
第一步:注册连接并提供服务:Selector 中初始化注册并建立连接事件,提供给Client建立连接的服务;client通过Selector连接与AcceptorHandler 发送创建客户端的连接,并又AcceptorHandler方法返回响应结果;同时该AcceptorHandler方法向Selector中注册可读事件(Client连接客户端成功后)
第二步:提供服务:client 发送请求,Selector中对注册状态及客户端可读性验证,正常情况下,已通过第一步,所以直接连接到处理器进行读写操作,根据需求执行业务逻辑,并响应给客户;
第三步:客户端连接可读,在向client响应客户端请求后,注册连接可读事件到Selector 中,所注册的是具体执行业务的Handler
6、NIO网络模型优化
-
非阻塞IO模型,服务器端提供一个单线程的selector来统一管理所有客户端接入的连接,并负责监听每个连接所关心的事件
-
弹性伸缩能力加强,服务器端一个线程处理所有客户端的连接请求,客户端的个数与服务器端的线程数呈M比1的关系
-
单线程节省资源,避免了线程的频繁创建和销毁,同时也避免了多个线程之间上下文的切换,提高了执行效率
二、NIO网络编程详解
NIO核心
- Channel:通道
- Buffer : 缓冲区
- Selector : 选择器 或 多路复用器
1、NIO核心类之Channel
Channel 具有双向性、非阻塞性、操作唯一性。
channel是信息传输的通道,是jdk NIO中对输入输出方法的另一种抽象,类比IO中的流,与流不同之处在于,channel具有双向性,而流是单向传输的,一个流必须是InputStream或OutputStream的子类,而通道可以用于读写或二者同时进行。channel可以工作在非阻塞模式下,构成NIO的基础。在NIO中,操作channel的唯一方式是使用buffer,通过buffer操作channel,实现数据块的传输。
channel的实现:
- 文件类:FileChannel,用于对文件的读写
- UDP类:DatagramChannel, 用于UDP的数据读写
- TCP类:ServerSocketChannel/SocketChannel,用于TCP的数据读写
Socket的使用:
Channel的使用:
2、NIO核心类之Buffer
Buffer:缓冲区,它提供唯一与channel进行交互的方式,作用是读写channel中的数据。Buffer从本质上说是一块内存区域,它是一块可以写入数据,读取数据的内存。
Buffer的属性
Capacity:分配的buffer容量,一旦写入的最大字节数超过这个容量,需要将其清空之后,才能继续往里面写数据
Position:当前操作的位置,初始值为0,最大值:容量值-1
Limit:上限,写模式下等于Capacity,读模式下等于最多能读取的数据
Mark:标记,记录mark的位置,调用reset方法时position会回到mark的位置
Buffer的使用:
/*
* 初始化长度为10的byte类型 buffer
*/
ByteBuffer.allocate(10);
/*
* 向byteBuffer中写入三个字节
*/
byteBuffer.put("abc".getBytes(Charset.forName("UTF-8")));
/*
* 将byteBuffer从写模式切换成读模式
*/
byteBuffer.flip():
/*
* 将b先调用get方法读取下一个字节
* 再调用reset方法将position重置到mark位置
*/
byteBuffer.get():
byteBuffer.reset();
/*
* 调用clear方法,将所有属性重置
*/
byteBuffer.clear();
3、NIO核心类之Selector
Selector 选择器/多路复用器
作用:I/O就绪选择
地位:NIO网络编程的基础
SelectionKey 选择键
四种就绪状态常量:连接就绪、接受就绪、读就绪、写就绪
Selector的使用:
最重要的应该就是代码片段3: 在这一步对就绪事件进行监听,如果一直不通过,就一直阻塞等待,直到有就绪事件发生,并注册通过检测有效,方才放行
4、NIO编程实现步骤
第一步:创建Selector
第二步:创建ServerSocketChannel,并绑定监听端口
第三步:将Channel设置为非阻塞模式
第四步:将Channel注册到Selector上,监听连接事件
第五步:循环调用Selector的select方法,检测就绪情况
第六步:调用selectedKeys方法获取就绪channel集合
第七步:判断就绪事件种类,调用业务处理方法
第八步:根据业务需要决定是否再次注册监听事件,重复执行第三步操作