io、nio、netty进化史

传统IO(BIO)

在传统IO中,服务端会首先创建一个serverSocket对象,然后新建一个接收新连接的线程(此线程死循环,需要不停接收新连接),当接收每一个新连接后,就会为新连接创建一个线程(此线程也为死循环,目的是为了不断监测这条连接上是否有数据可读),用于读取数据。最后读取数据以字节流的形式。

从这样一个服务端逻辑可以出看出一下问题:

1.线程资源受限:线程资源十分宝贵,同一时刻有大量线程处于阻塞状态是非常严重的资源消耗,操作系统负担不起。

2.线程切换效率低下:线程爆炸之后操作系统会进行频繁的线程切换,应用性能急剧下降。

3.数据读写效率低下:数据读写时是字节流为单位,效率不高。

 

 

 

NIO

通过以上的问题,JDK在1.4之后提出NIO编程。NIO为非阻塞性IO

解决线程资源受限的问题:

在NIO的编程模型中,新来的一个连接不再创建一个新的线程,而是可以把这条连接直接绑定在某个固定的线程,然后这条连接所有的读写都由这个线程来负责。NIO是怎么做到的呢?

 NIO 模型中,他把这么多 while 死循环变成一个死循环,这个死循环由一个线程控制,那么他又是如何做到一个线程,一个 while 死循环就能监测1w个连接是否有数据可读的呢? 这就是 NIO 模型中 selector 的作用,一条连接来了之后,现在不创建一个 while 死循环去监听是否有数据可读了,而是直接把这条连接注册到 selector 上,然后,通过检查这个 selector,就可以批量监测出有数据可读的连接,进而读取数据,下面我再举个非常简单的生活中的例子说明 IO 与 NIO 的区别。

在一家幼儿园里,小朋友有上厕所的需求,小朋友都太小以至于你要问他要不要上厕所,他才会告诉你。幼儿园一共有 100 个小朋友,有两种方案可以解决小朋友上厕所的问题:

每个小朋友配一个老师。每个老师隔段时间询问小朋友是否要上厕所,如果要上,就领他去厕所,100 个小朋友就需要 100 个老师来询问,并且每个小朋友上厕所的时候都需要一个老师领着他去上,这就是IO模型,一个连接对应一个线程。

所有的小朋友都配同一个老师。这个老师隔段时间询问所有的小朋友是否有人要上厕所,然后每一时刻把所有要上厕所的小朋友批量领到厕所,这就是 NIO 模型,所有小朋友都注册到同一个老师,对应的就是所有的连接都注册到一个线程,然后批量轮询。

这就是 NIO 模型解决线程资源受限的方案,实际开发过程中,我们会开多个线程,每个线程都管理着一批连接,相对于 IO 模型中一个线程管理一条连接,消耗的线程资源大幅减少.

解决线程切换效率低下的问题:

由于 NIO 模型中线程数量大大降低,线程切换效率因此也大幅度提高。

解决IO读写效率低下:

NIO 解决这个问题的方式是数据读写不再以字节为单位,而是以字节块为单位。IO 模型中,每次都是从操作系统底层一个字节一个字节地读取数据,而 NIO 维护一个缓冲区,每次可以从这个缓冲区里面读取一块的数据, 这就好比一盘美味的豆子放在你面前,你用筷子一个个夹(每次一个),肯定不如用勺子挖着吃(每次一批)效率来得高。

NIO的核心思路:

  1. NIO 模型中通常会有两个线程,每个线程绑定一个轮询器 selector ,在我们这个例子中serverSelector负责轮询是否有新的连接,clientSelector负责轮询连接是否有数据可读
  2. 服务端监测到新的连接之后,不再创建一个新的线程,而是直接将新连接绑定到clientSelector上,这样就不用 IO 模型中 1w 个 while 循环在死等
  3. clientSelector被一个 while 死循环包裹着,如果在某一时刻有多条连接有数据可读,那么通过 clientSelector.select(1)方法可以轮询出来,进而批量处理
  4. 数据的读写以内存块为单位

 

那么,有了NIO为什么还要有netty呢?

 

  1. JDK 的 NIO 编程需要了解很多的概念,编程复杂,对 NIO 入门非常不友好,编程模型不友好,ByteBuffer 的 Api 简直反人类
  2. 对 NIO 编程来说,一个比较合适的线程模型能充分发挥它的优势,而 JDK 没有给你实现,你需要自己实现,就连简单的自定义协议拆包都要你自己实现
  3. JDK 的 NIO 底层由 epoll 实现,该实现饱受诟病的空轮询 bug 会导致 cpu 飙升 100%
  4. 项目庞大之后,自行实现的 NIO 很容易出现各类 bug,维护成本较高,上面这一坨代码我都不能保证没有
  5. Netty 底层 IO 模型随意切换,而这一切只需要做微小的改动,改改参数,Netty可以直接从 NIO 模型变身为 IO 模型
  6. Netty 自带的拆包解包,异常检测等机制让你从NIO的繁重细节中脱离出来,让你只需要关心业务逻辑
  7. Netty 底层对线程,selector 做了很多细小的优化,精心设计的 reactor 线程模型做到非常高效的并发处理
  8. 自带各种协议栈让你处理任何一种通用协议都几乎不用亲自动手

提一手:

初学 Netty 的时候,由于大部分人对 NIO 编程缺乏经验,因此,将 Netty 里面的概念与 IO 模型结合起来可能更好理解

boss 对应 IOServer.java 中的接受新连接线程,主要负责创建新连接

worker 对应 IOClient.java 中的负责读取数据的线程,主要用于读取数据以及业务逻辑处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值