BIO实战、NIO编程与直接内存、零拷贝深入辨析
长连接、短连接
- 长连接
- socket连接后不管是否使用都会保持连接状态
- 多用于操作频繁,点对点的通讯,避免频繁socket创建造成资源浪费,比如TCP
- 短连接
- socket连接后发送完数据后就断开
- 早期的http服务采用的短连接
网络编程
-
网络编程
-
BIO
- 一个连接一个线程,客户端有连接请求时服务端就需要启动一个线程进行处理,线程开销大
- BIO是面向流的,各种流都是阻塞的,并且是单向的
- 一个连接一个线程,客户端有连接请求时服务端就需要启动一个线程进行处理,线程开销大
-
伪异步IO
- 将请求连接放入线程池,一对多,但线程依旧是宝贵资源
-
NIO
- 一个请求一个线程,但客户端发送的请求连接都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理
- NIO是面向缓冲区的、非阻塞,并且channel是双向的
- 基于事件驱动模型、单线程处理多任务
- 非阻塞I/O,I/O读写不再阻塞
- 基于块的传输比基于流的传输更高效,基于零拷贝保证网络传输速度
- 基于Reactor线程模型,在Reactor模型中,事件会提前注册回调函数,事件分发器在等待该事件发生,事件分发器就将该事件交给对应的回调函数进行处理读写操作
- 一个请求一个线程,但客户端发送的请求连接都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理
-
AIO
- 一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务端去启动线程进行处理
-
-
问题
: NIO与BIO的区别-
NIO是面向缓冲区的,BIO是面向流的
-
流
- 从流中读一个或多个字节直到全部有序被读出,不能前后移动数据
-
缓冲区
- 通过将读取的数据缓存到缓冲区可以进行适度的移动操作
-
-
NIO是非阻塞的,BIO是阻塞的
- 阻塞
- 当线程需要读写时,需要等待操作结束后,才能继续后续的操作
- 非阻塞
- 当线程需要读写时,一旦收到读写请求,会将读写交给提前注册好的回调函数进行处理,所以单线程能处理更多的任务
- 阻塞
-
NIO的三大组件
- 选择器(Selector)
- 允许单独的一个线程来监视多个管道,可以注册多个管道使用一个选择器,然后使用一个单独的线程操作这个选择器
- 管道(Channel)
- 主要是负责程序与OS的读写交互
- 缓冲区(Buffer)
- 用于和管道进行交互,数据通过管道读入缓冲区或从缓冲区写入管道中
直接内存
- 在NIO下采用直接内存(DirectByteBuffer)进行操作,一般速度比堆内存(HeapByteBuffer)快很多
- 直接内存并不是JVM的内存区域,而是通过本地库获取到的堆外内存
- 直接内存(堆外内存)与堆内存比较
- 直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
- 直接内存 IO 读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显
传统数据传送机制
- 从磁盘读取内核态缓冲区,从内核态缓冲区拷贝到用户态缓冲,用户态再读取缓冲数据拷贝到socket,socket将数据拷贝到网卡进行网络传输
零拷贝
- 执行读写操作时,CPU不需要先将数据从某处内存复制到另一个特定区域,通过减少中间复制操作来提升网络传输速度
- 可以通过DMA(直接内存访问)减少内核态与内核态的交互过程
mmap
- 通过磁盘文件位置与程序的缓冲区建立映射关系,减少拷贝次数
- 3次拷贝(1次CPU拷贝、2次DMA拷贝)以及4次上下文切换
sendfile
- 通过向内核态发送sendfile指令操控它进行文件复制
- 3次拷贝(1次CPU拷贝,2次DMA拷贝)以及2次上下文切换
splice
- 数据从磁盘读取到OS内核缓冲区后,在内核缓冲区直接可将其转成内核空间其他数据buffer,而不需要拷贝到用户空间
- 2次拷贝(2次DMA拷贝)以及2次上下文切换
Netty的零拷贝实现
- 在网络通信上,接收和发送数据通过直接内存进行操作
- 在缓存操作上,通过合并多个缓冲数据,避免重复拷贝
- 在文件传输上,直接通过文件缓冲数据传输到管道,避免拷贝