资讯专栏INFORMATION COLUMN

Java 网络 IO 模型

Loong_T / 670人阅读

摘要:有一个神奇的特性当发起操作之后,线程不用等待读取完毕,而是可以直接返回,继续执行其他操作。如果使用,一个线程可以同时发起多个操作,这就意味着,一个线程可以同时处理多个请求。

在进入主题之前先看个 Java 网络编程的一个简单例子:代码很简单,客户端和服务端进行通信,对于客户端的每次输入,服务端回复 get。注意,服务端可以同时允许多个客户端连接。

服务端端代码:

// 创建服务端 socket
ServerSocket serverSocket = new ServerSocket(20000);
client = serverSocket.accept();

// 客户端连接成功,输出提示
System.out.println("客户端连接成功");
// 启动一个新的线程处理客户端请求
new Thread(new ServerThread(client)).start();


// 子线程中处理客户端的输入
class ServerThread implements Runnable {
    .....
    @Override
    public void run() {
        boolean flag = true;
        while (flag) {

            // 读取客户端发送来的数据
            String str = buf.readLine();
            
            // 回复给客户端 get 表示收到数据
            out.println("get"); 
        }
    }
}

客户端代码 :

Socket client = new Socket("127.0.0.1", 20000);
boolean flag = true;
while (flag) {
    
    // 读取用户从键盘的输入
    String str = input.readLine();
    // 把用户的输入发送给服务端
    out.println(str);

    // 接受到服务端回传的 get 字符串
    String echo = buf.readLine();
    System.out.println(echo);
    }
}

考虑到完整的 Java 示例代码太过庞大影响阅读,所以这里不完整贴出,如果需要在 github 直接下载,这里是下载地址。

可以看到,server 为了能够同时处理多个 client 的请求,需要为每个 client 开启一个 thread,这种 one-thread-per-client 的模式对于 server 而言压力是很大的。假设有 1k 个 client,对应的 server 应该启动 1k 个 thread,那么 server 所耗费的内存,以及 thread 切换时候占用的时间等等都是致命伤。即使使用线程池的技术来限制线程个数,这种 blocking-IO 的模型还是没办法支撑大量连接。

每个 client 都需要一个 thread 来请求处理。

NIO

上面这种 one-thread-per-client 的模式无法支撑大量连接的主要原因在于 readLine阻塞 IO,即在 readLine 没能够读取到数据的时候,会一直阻塞线程,使得线程无法继续执行,那么 server 为了可以同时处理多个 client,只能同时开启多个线程。

所以,Java 1.4 之后引入了一套 NIO 接口。NIO 中最主要的一个功能就是可以进行非阻塞 IO 操作:如果没能够读取到数据,非阻塞 IO 不会阻塞线程,而是直接返回 0。这种情况下,线程通过返回值判断数据还没有准备好,就可以处理其他事情,而不会被阻塞。

上图是阻塞 IO 和非阻塞 IO 的区别,可以看出虽然 非阻塞 IO 并不会被阻塞,但是它仍然不断的调用函数检查数据是否已经可读,这种现象在代码中是以这种形式展现:

while((str = read()) == 0) {
   
}
 // 继续读取到数据之后的逻辑。

可以明白,虽然非阻塞 IO 不会阻塞线程,但是由于没有数据可读,线程也没有办法继续执行下面的逻辑,只能不断的调用判断,等待数据到来。这种情况下称为同步 IO。所以综上,NIO 本质上是一个非阻塞同步 IO

IO 复用

由于 NIO 不会因为数据还没有到达而被阻塞,那么就没有必要每一个 client 都分配一个 thread 不断去轮询判断是否有数据可读。可以使用一个 thread 监听所有的 client 连接,由这个 thread 循环判断是否有某个 client 的数据可读,如果有就告知其他 thread 某个 client 连接由数据可读。这种行为就被称之为 IO 复用。 在 NIO 中提供了 Selector 类来监听所有 client 连接是否有数据可读。

使用 Selector 来实现 IO 复用,只有一个 thread 需要关心数据是否到来,其他线程等待通知就好。如此一来,只有监听线程会一直循环判断,并不会占据太多 CPU 资源。提到 NIO 中的 Selector,不得不说一下 Linux 编程中的 IO 复用,因为 NIO 中的 Selector 底层就是使用系统级的 IO 复用方案。

Linux 系统的 IO 复用实现方案有 2 种:

select

epoll

在 Linux 2.6+ 的版本上 NIO 底层使用的是 epoll,在 2.4.x 的版本使用的是 select 函数。epoll 函数在性能方面比 select 好很多,这里可以不关心 Linux 编程具体细节。值得一提的是,Java 的 netty 网络框架底层就是使用 NIO 技术。

AIO

回顾一下 NIO 中:使用监听线程调用 select 函数来监听所有请求是否有数据到达,如果有数据则通知其他线程来读取数据。这里在线程读取数据的过程中,线程在数据没有读取完毕之前是处于阻塞状态,只有数据读取完毕之后线程才可以继续执行逻辑。之前说过,这种称之为同步 IO。JDK 7 中新增了一套新接口 AIO(Asynchronous IO)。

AIO 有一个神奇的特性:当发起 IO 操作之后,线程不用等待 IO 读取完毕,而是可以直接返回,继续执行其他操作。等到数据读取完毕之后,系统会通知线程数据已经读取完毕。这种发起 IO 操作,但是不必等待数据读取完毕的 IO 操作称之为异步 IO。如果使用 AIO,一个线程可以同时发起多个 IO 操作,这就意味着,一个线程可以同时处理多个请求。著名的 web 服务器 Nginx 就是用了异步 IO。关于更多的细节,可以参考下我的另一篇文章 End

到目前为止,文章解释了阻塞/非阻塞 IO,同步/异步 IO 的区别,谈起 IO 模型,不可避免会涉及 Linux 的 5 种 IO 模型

阻塞 IO

非阻塞 IO

IO 复用

信号驱动 IO

异步 IO

除去信号驱动 IO没有提及,其他 4 种主要的 IO 模型都有所解释,理解了这些 IO 模型的概念对于编写代码有很大的帮助。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70605.html

相关文章

  • java同步非阻塞IO

    摘要:的异步即是异步的,也是非阻塞的。但是,也可以进行一层稍微薄点的封装,保留这种多路复用的模型,比如的,是一种同步非阻塞的模型。系统调用操作系统的系统调用提供了多路复用的非阻塞的系统调用,这也是机制实现需要用到的。 异步IO编程在javascript中得到了广泛的应用,之前也写过一篇博文进行梳理。js的异步IO即是异步的,也是非阻塞的。非阻塞的IO需要底层操作系统的支持,比如在linux上...

    caoym 评论0 收藏0
  • JDK10都发布了,nio你了解多少?

    摘要:而我们现在都已经发布了,的都不知道,这有点说不过去了。而对一个的读写也会有响应的描述符,称为文件描述符,描述符就是一个数字,指向内核中的一个结构体文件路径,数据区等一些属性。 前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 本来我预想是先来回顾一下传统的IO模式的,将传统的IO模式的相关类理清楚(因为IO的类很多)。 但是,发现在整理的过程已...

    YFan 评论0 收藏0
  • Java NIO浅析

    摘要:阻塞请求结果返回之前,当前线程被挂起。也就是说在异步中,不会对用户线程产生任何阻塞。当前线程在拿到此次请求结果的过程中,可以做其它事情。事实上,可以只用一个线程处理所有的通道。 准备知识 同步、异步、阻塞、非阻塞 同步和异步说的是服务端消息的通知机制,阻塞和非阻塞说的是客户端线程的状态。已客户端一次网络请求为例做简单说明: 同步同步是指一次请求没有得到结果之前就不返回。 异步请求不会...

    yeooo 评论0 收藏0
  • Java网络编程-你是GG还是MM?

    摘要:网络层主要将从下层接收到的数据进行地址例的封装与解封装。会话层通过传输层端口号传输端口与接收端口建立数据传输的通路。 第六阶段 网络编程 每一台计算机通过网络连接起来,达到了数据互动的效果,而网络编程所解决的问题就是如何让程序与程序之间实现数据的通讯与互动在吗?你是GG还是MM? (一) 网络模型概述 (1) 两大模型 网络模型一般是指: OSI(Open System Inter...

    Shihira 评论0 收藏0
  • NIO网络相关基础知识

    摘要:操作系统是能够获取到事件操作完成的事件,基于回调函数机制和操作系统的操作控制实现事件检测机制。 前面的文章NIO基础知识介绍了Java NIO的一些基本的类及功能说明,Java NIO是用来替换java 传统IO的,NIO的一些新的特性在网络交互方面会更加的明显。 Java 传统IO的弊端     基于JVM来实现每个通道的轮询检查通道状态的方法是可行的,但仍然是有问题的,检查每个通道...

    1fe1se 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<