什么是经典的三种I/O模式
我们在学Netty的时候讲的最多的就是Reactor的三种模式,因为他的这个模式用的最广泛,涉及的I/O知识点很基础也很常用。
- 我们去吃饭的时候:
1:家乐园排队打饭,排队在窗口,打好才走。
2:点单,拿一个号码牌等待被叫,好了自己去端走。
3:辣可可包厢模式,点单后菜直接端上桌。
- 类比:
饭店->服务器
饭菜->数据
饭菜好了->数据就绪
端菜、送菜->读取数据
模式 | 特点 | JDK版本 |
---|---|---|
排队打饭模式 | BIO(阻塞) | JDK1.4之前 |
点单被叫模式 | NIO(非阻塞) | JDK1.4 |
包厢消费模式 | AIO(异步) | JDK1.7 |
-
阻塞与非阻塞区别:
菜没好,要不要死等 -> 数据就绪之前要不要等待。
阻塞:没有数据传过来时,读会阻塞直到有数据;缓冲区满时,写操作也会阻塞。非阻塞:遇到这些情况都是直接返回。
-
同步与异步区别:
菜好了,谁端 -> 数据就绪后,数据操作谁来完成?
数据就绪后需要自己去读就是同步,数据就绪后直接读好再回调给程序是异步。
总结
所以BIO是阻塞同步方式;NIO是非阻塞同步方式;AIO是非阻塞异步方式。
疑问
Netty为什么仅仅支持NIO模式呢?为什么不用AIO模式呢?(Netty删掉了AIO的实现)
1:Windows实现AIO很成熟,但Win很少用来做服务器。
2:Linux常用来做服务器,但AIO实现不够成熟。
3:Linux下的AIO相比较NIO的性能提升不够明显。
Reactor线程模型
生活场景 | 饭店规模变化 |
---|---|
1:一个人包揽所有 | 迎宾、点菜、做饭、上菜、送客等。 |
2:多招几个伙计 | 大家一起做上面的事情。 |
3:进一步分工 | 选一个或多个人专门做迎宾。 |
Netty中的Reactor线程模型有三种,分别如下:
- Reactor单线程模型;
- Reactor多线程模型;
- 主从Reactor多线程模型;
–传统单线程模型–
单线程模型简单,一个线程就一路走到底,优点是开发简单;缺点是如果某一个环节慢了,整个线程资源就拖垮了,甚至应用也会处理相当慢。
这个模型中最主要的就是每一步都是阻塞的,也就是BIO模型。有朋友会说改造成多线程不就解决了吗,多线程也只是用更多的线程做同样的事情而已,对应的每一步还是阻塞的。
Reactor单线程模型
注:Netty将这些读写、编解码等操作包装成事件,想想NIO的非堵塞的原理和包装成事件有何必然关联性。
有同学会问,这个绿色的圈怎么理解,其实如上所说的,netty把所有的请求操作内部操作都认定为事件,绿色是一个客户端连接事件,这个连接事件也是这三种模型的关键优化点。
上面的单线程Reactor模式因为服务端只有一个线程处理IO和业务逻辑,服务端性能肯定受到限制。虽然用了NIO的非阻塞方式,但一个线程如果挂了,这个应用基本也就挂了。因此就有了多线程版本。
Reactor多线程模型
如下图所示,Reactor还是一个线程,负责监听IO事件以及分发,业务逻辑处理部分使用了一个线程池来进行处理(解码、计算、编码),这样就解决了服务端单线程处理请求而带来的性能瓶颈。就如生活场景中说的第二点,找多个人一起干同样的事。
但是这样还是有问题,这样会把性能的瓶颈转移到IO处理上。因为IO事件的监听和分发采用的还是单个线程,在并发量比较高的情况下,这个也是比较影响性能的。这是否还有继续优化的空间呢?
主从多线程模式
我们知道Reactor主要是负责IO事件的监听和分发。单个Reactor单个线程这种模式在并发量比较高的情况下,会存在性能瓶颈。我们可以想象一下生活场景,开饭店最重要的是什么?我觉得是迎宾,找几个漂亮的小姑娘在门口,把客人先招揽进来,咱们做菜慢一点又有什么关系呢?流量都没进来,你的应用再强大饭菜做的再香也没有任何体现的价值。
再次提到这个绿色的连接事件,第一和第二两种模型为啥会进化成主从模式,你想一想,客户端连接上来都会立即传输数据吗?都会立即发送数据交互请求吗?不一定吧。
用生活场景说:用户到饭店门口了,如果我们都在店里面忙不过来,没有人招呼外面的客人会怎么样?客人就会走吧。连接也一样,如果线程都在里面忙碌或者卡住了,那么客户端连接就会超时。
再结合起来看看,我们第一步就先要保证和客户端连接稳定,而且客户端连接这个操作是不耗啥资源的,很快就完成了,先和客人建立了联系,客人就不会走。大家想想是不是这个道理。
所以对于服务器而言最重要的是什么呢?是“接收连接”这个事情。主从模式更好的模式提高了性能利用率,他把acceptor单独注册到mainReactor里面去做接收连接事件。
最后
我们所以的Netty服务端开发都是用了主从Reactor模式开编写,虽然说他不是最优的,比如我的业务就是只要几个连接就能完成事情,比如一个线程像一个VIP服务一样,也不会占用更多资源;或者这个项目就目前属于维护型项目,就不用什么改动了,就算改了也没有明显的效率提升反而增加了上线测试风险。但是考虑未来更多的可能性,都是建议使用第三种方式。
分析问题要从场景来分析,并非BIO就弱于NIO。