Java IO模型

一、BIO(blocking I/O)

定义

同步阻塞模型,客户端发一次链接请求,服务端就要新建一个线程去进行处理。当建立了请求,但是这个线程却没有处理业务逻辑,导致大量的空闲。Bio模型如下图:
asda

优点

单个线程负责单个客服端,因此响应的时间比较短。比较适合对服务器资源要求较高的请求。简单容易理解。

缺点

如今强调高并发多线程的时代,线程资源又及其的宝贵。如果创建大量的线程,会浪费大量的线程资源,导致系统性能变慢。

二、 NIO (Non-blocking IO)

定义

同步非阻塞,同步即消息接收方或发送方会一直等待消息处理完成。非阻塞就是消息接收方或发送方在等待的过程中,会得到任务是否完成的结果。如下图所示:
在这里插入图片描述
如上图,用户线程会一直轮询数据是否到达。服务端也会返回数据是否到达的结果给用户线程。

多路复用模型

多路复用的模型是基于NIO模型。如果一个请求到来,服务端就创建一个线程去处理请求。这么看来,那么和BIO也同样很耗费资源。但是,如果用一个专门的线程轮询处理到来的连接,然后让发送方先将数据发送到缓冲区,当数据发送完成时,通知对应的业务线程去处理。这样,只需要一个线程去轮询等待,其他的线程还能干别的业务。这种方式就是多路服用模型。

多路复用的核心组件

Selector

选择器,也就是将到来的不同请求,分配给对应的线程去处理。

为什么可以做到上面的功能呢?
1.多个通道(Channel)会以事件的方式(Accept、Read、Write)注册到同一个Selector中,Selector能够轮询的检测多个通道上是否有事件发生。如果有事件发生,便会获取事件并对每个事件进行相应的处理。这样就可以管理多个连接和请求。

在这里插入图片描述
Channel:
通道可以同时进行读写,而流只能读或者写,通道可以实现异步读写数据(主要是因为通道连着缓冲区)。通道可以读取缓冲数据,也可以写数据到缓冲区中。
当客户端请求服务端时,服务端会为客户端建立一条通道。并且会将通道绑定到selector事件中。
在这里插入图片描述

Buffer:
缓冲区,通道中的数据会先放到buffer中,当数据读取完成时,等待相应的业务线程去处理。如下图
在这里插入图片描述

多路复用优点

NIO由原来的阻塞读写(占用线程)变成了单线程轮询事件,找到可以进行读写的网络描述符进行读写。除了事件的轮询是阻塞的(没有可干的事情必须要阻塞),剩余的I/O操作都是纯CPU操作,没有必要开启多线程。

并且由于线程的节约,连接数大的时候因为线程切换带来的问题也随之解决,进而为处理海量连接提供了可能。

单线程处理I/O的效率确实非常高,没有线程切换,只是拼命的读、写、选择事件。但现在的服务器,一般都是多核处理器,如果能够利用多核心进行I/O,无疑对效率会有更大的提高。

reactor --多路复用具体实现

单reactor多线程

在这里插入图片描述
上面大致可以分为:
1)reactor对象通过select(等同于前面的selector)监控客户端请求,并通过dispatch分发
2)如果是accept请求,就建立连接,并将建立的事件(write read等事件)注册到select上
3)如果其他的事件,就交给相应的handler进行处理。handler只负责响应事件,不做具体的业务处理。handler调用相应的worker线程池中的某个线程处理业务。
4)worker中的线程处理完成后,会返回给handler,最后handler通过send将结果返回给client。

缺点: reactor处理所有的事件的监听和响应,在单线程运行及高并发条件下,容易出现性能瓶颈。

主从reactor模型

在这里插入图片描述
如上图,为了解决单reactor的问题。主从reactor中的主reactor专门负责处理accept请求。其他的和单reactor一样。

Netty改进后的Reactor模型

图片纠正:以下的NioEventGroup都要把Group去掉,即NioEventLoop
在这里插入图片描述
BossGroup: 类似于主从reactor中的主reactor,只不过BossGroup中可以创建多个NioEvent循环组。通过selector监听Client中事件连接事件,并将来不及处理的事件放入TaskQueue中。

WorkerGroup: 类似于主从Reactor中的从Reactor,监听BossGroup中注册的读写事件,并交给handler进行处理。

NioEventLoop: BossGroup、WokerGroup中可以包含多个NioEventLoop。每个NioEventLoop包含三个步骤:1.Accept 2.processSelectKeys 3.runAllTasks。 三个事件一直循环往复。

Channel: 传输数据的通道,以及注册到selector。并且被selector监听的对象。

PipeLine: 处理数据的管道,类似于生活中的过滤器,只不过管道是对数据进行过滤和处理。里面封装大量的ChannelHandler。

ChannelHandler: 对流经的数据进行处理。pipeline之所以能对数据进行处理,主要是含有大量的ChannelHandler。
在这里插入图片描述
ChannelHandlerContext: 对ChannelHander进行了封装。

Netty改进后的reactor处理流程如下:
1.BossGroup中的NioEventLoop的Selector对象监听客户端的Accept连接事件,并将对应的read/write事件通过NioSocketChannel注册到WorkerGroup中的NioEventLoop。
2.NioEventLoop(WorkerGroup)通过selector监听NioSocketChannel中到来的读写事件的I/O,并将对应事件的真正处理业务交给pipiline去处理
3.PipeLine中含有大量的ChannelHandler,可以去处理流经的数据。

注:上面的taskQueue是用来保存来不及处理的事件。NioEventLoop执行到了(step3)runAlltask就会去处理队列中的消息。并一直轮询step1、step2、step3.

三、AIO

定义:

异步非阻塞I/O,整个过程中不存在阻塞。即,请求端发送请求给服务端,请求端可以做自己的事情。等数据到达内核区时,操作系统会自动把数据保存到用户内存,然后再通知请求用户I/O已经完成。最后,发送端可以开始业务处理。
在这里插入图片描述

标准/典型的Reactor:
步骤1:等待事件到来(Reactor负责)。
步骤2:将读就绪事件分发给用户定义的处理器(Reactor负责)。
步骤3:读数据(用户处理器负责)。
步骤4:处理数据(用户处理器负责)。

改进实现的模拟Proactor:
步骤1:等待事件到来(Proactor负责)。
步骤2:得到读就绪事件,执行读数据(现在由Proactor负责)。
步骤3:将读完成事件分发给用户处理器(Proactor负责)。
步骤4:处理数据(用户处理器负责)。

Proactor与Reactor的区别

可以看出,两个模式的相同点,都是对某个I/O事件的事件通知(即告诉某个模块,这个I/O操作可以进行或已经完成)。在结构上,两者也有相同点:事件分发器负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler;不同点在于,异步情况下(Proactor),当回调handler时,表示I/O操作已经完成;同步情况下(Reactor),回调handler时,表示I/O设备可以进行某个操作(can read 或 can write)。

Buffer的选择
通常情况下,操作系统的一次写操作分为两步: 1. 将数据从用户空间拷贝到系统空间。 2. 从系统空间往网卡写。同理,读操作也分为两步: ① 将数据从网卡拷贝到系统空间; ② 将数据从系统空间拷贝到用户空间。

对于NIO来说,缓存的使用可以使用DirectByteBuffer和HeapByteBuffer。如果使用了DirectByteBuffer,一般来说可以减少一次系统空间到用户空间的拷贝。但Buffer创建和销毁的成本更高,更不宜维护,通常会用内存池来提高性能。

如果数据量比较小的中小应用情况下,可以考虑使用heapBuffer;反之可以用directBuffer。

总结

传统的BIO里面socket.read(),如果TCP RecvBuffer里没有数据,函数会一直阻塞,直到收到数据,返回读到的数据。

对于NIO,如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。

最新的AIO(Async I/O)里面会更进一步:不但等待就绪是非阻塞的,就连数据从网卡到内存的过程也是异步的。

换句话说,BIO里用户最关心“我要读”,NIO里用户最关心”我可以读了”,在AIO模型里用户更需要关注的是“读完了”。

NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。
使用NIO != 高性能,当连接数<1000,并发程度不高或者局域网环境下NIO并没有显著的性能优势。

NIO并没有完全屏蔽平台差异,它仍然是基于各个操作系统的I/O系统实现的,差异仍然存在。使用NIO做网络编程构建事件驱动模型并不容易,陷阱重重。

推荐大家使用成熟的NIO框架,如Netty,MINA等。解决了很多NIO的陷阱,并屏蔽了操作系统的差异,有较好的性能和编程模型。

引用:link1
link2
尚硅谷

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值