BIO到NIO的演变过程

时间:2022-07-23
本文章向大家介绍BIO到NIO的演变过程,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

学习任何东西之前都得知道他是为什么而产生的。任何一个设计,或技术。都是为了解决某个或多个问题而产生的。即BIO到NIO到多路复用再到epollo 再到netty网络编程框架。今天我们来看看这个演进的过程。

IO的演进

下图是来自培训机构的一个大佬的图:

什么是IO?

  • 我自己的理解:从本地磁盘或者网络传输过来的数据的读取和写入的过程。也就是从网卡或者磁盘的数据到应用程序内部(JVM的动态内存中)的过程。这个过程是相当耗费时间的,看看下面的数据。随机访问相差10万倍。在性价比的驱动下,优化硬件的成本远远比优化软件这方面的成本高的时候或者还有其他原因的时候那么就去优化一下我们的访问方式。
1.顺序访问:这种情况下,内存访问速度仅仅是硬盘访问速度的6~7倍(358.2M / 53.2M = 6.7)

2.随机访问:这种情况下,内存访问速度就要比硬盘访问速度快上10万倍以上 (36.7M / 316 = 113,924)

在看各个IO之前先理解一下这个过程linux是如何实现的: 在linux系统中一切皆文件。在 系统中启动一个服务的时候,我们会为其定义端口号代表这个服务,启动成功后linux就会创建一个监听,每接入一个clinet就会被我们的服务监听到,然后去内核拿这个数据。 重要的就是图中的fd(文件描述符)和内核交互的过程中就是根据linux系统中的这个文件描述符来确定是那个客户端的。

1.BIO

  1. BIO也就是阻塞IO的意思,这里的阻塞是指的应用程序监听到服务器有端口连接进来,在与内核进行交互的时候是阻塞的。看下面的图理解一下:

如图上红色线的部分。我们监听到有个client进来的时候,于是我们起一个线程去请求是否有数据进来,此时这个阶段是阻塞的,没有数据返回就会一直等待。当又有新的客户端进来的时候我们只能再起新的进程了。那就使用多线程的方式再起新的线程去请求。

  1. 毫无疑问,这是有问题的,我们都知道创建一个线程会耗费内存的。所以创建多个线程有可能会产生OOM的。
  2. 有人就说了我们起一个线程池,搞一个没有核心线程数的线程池。只有最大线程数,然后设置过期时间,是个解决办法。但是不合理。同时涌入上万甚至更多得用户,肯定得蹦
  3. 那搞一个非阻塞的线程呗。 在JDK1.4 NIO登场。

2.NIO

  1. 从上文中可知,NIO解决的问题之一是不让创建多个线程。那NIO的设计这给出的办法就是让和内核交互的这个过程中是非阻塞的。不管他有没有数据进来请求后就会返回。 2. 但是得一直轮训接入的客户端的文件描述符,查看是否有数据进来。看下图品一下:
  1. 从上面可以看出,解决了之前必须创建多个线程的问题。现在我们再看看在这个模型下会又有什么问题呢?for循环便利那岂不是很耗费CPU资源啊,当我们有N多个客户端进来给你CPU打满,那其他的client也不就是死。所以这又出现问题了。
  2. 于是又有大佬站出来了,提出了IO的多路复用原则。

3. NIO+多路复用

  1. 由于在NIO中是非阻塞的,现在主要问题是我们并不知道哪些客户端会有数据过来,我们得遍历所有客户端的来查看是否有数据,那就是我们如何才能让服务程序能知道哪些客户端有数据了,需要去读取。大佬在内核实现了selector,epoll ,reactor等多路复用的线程模型。其中的思想就是可以通过他们提供的方法可以拿出就绪有数据过来的客户端fd。

总结

大概是过了一下从BIO到NIO+IO多路复用的演进(IO模型的演进)

  1. BIO 遇到创建多个线程有可能导致OOM,对系统的并发能力有极大限制
  2. NIO,导致CPU空转的,导致CPU做无用功。
  3. NIO+多路复用以及已有实现的NIO+多路复用的模型

思考,selector和epoll是如何实现可以知道有客户端的数据进来了,且通知应用服务去读区数据呢?

资源:

  • JavaNIO的具体实现 :https://juejin.im/entry/599f971af265da247d728531
  • 本文参考视频:https://www.bilibili.com/video/BV1cT4y1g79r?from=search&seid=2733076814654841327