java 异步模型_Java高效NIO之IO模型基础

概述

目前常见的面向IO操作编程模型有以下几种:

Blocking IO:阻塞IO

Non-Blocking IO:非阻塞IO

IO Multiplexing:IO多路复用

Asynchronous IO:异步IO

IO多路复用技术(又称:事件驱动IO),是为了解决传统同步阻塞IO模式下,服务端的每个线程(进程)只能给一个Client提供服务的问题,避免服务器创建大量线程(占用大量内存、线程切换开销)的问题而设计的。本文对各种网络IO编程模型进行简要的介绍。

本文主要讨论的场景是Linux下的网络IO,主要参考了Richard Stevens的《UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking API》和网络上的相关文章。

IO模型分类

先来了解下IO过程相关的同步、异步,阻塞、非阻塞等概念。在IO操作过程中涉及的对象包括:

调用IO的用户程序(线程或进程)

处理IO的内核程序(Kernel)

IO操作过程中包括两个阶段(以Read操作为例),分别是:

数据准备阶段:Kernel等待数据就绪

数据拷贝阶段:数据就绪后把数据从Kernel空间拷贝到用户程序空间

关于同/异步,非阻塞/阻塞,就是围绕两个对象和两个阶段展开的。

同步异步

同步、异步主要是指用户程序和 内核 的交互模式。

同步:用户程序触发IO操作后,通过一直等待或轮询的方式去检查内核IO操作是否就绪,直到IO就绪。

异步:用户程序触发IO操作后,干其他事情去了,当内核IO操作就绪后,内核把数据写入用户程序指定的Buffer。

从代码实现表现来看,IO操作后,同步模式下,用户需要自己不断去轮询内核获取结果;而异步模式下内核会回写数据到用户程序Buffer并通知用户程序。同步异步的关键:IO设备状态需要自己轮询,还是内核主动通知。

阻塞非阻塞

阻塞非阻塞,是程序执行IO操作的系统调用时,根据IO设备操作的就绪状态采取不同处理方式。

阻塞:当程序试图进行读写IO操作时,如果暂时没有可读数据或者不可写,程序就一直处于等待状态,直到可读或可写为止。

非阻塞:当程序试图进行读写IO操作时,如果无可读数据或不可写,程序马上返回,而不会等待。

从代码实现表现来看,IO操作后,阻塞模式下,方法挂起不返回,直到IO设备就绪操作成功,方法才返回;而非阻塞模式,调用方法不等待IO设备就绪就直接返回。阻塞非阻塞的关键:方法调用后是否立即返回。

阻塞IO(Blocking IO)

Linux中默认的Socket IO都是Blocking的,读数据的流程如下:

11185f639c096167d996b63d73fe4f91.png

当用户程序调用recvfrom系统调用时,Kernel开始IO的第一个阶段:数据准备。对于网络IO,此时数据很可能还在网络上传送,因此Kernel需要等待数据传输完成。

此时,用户程序会阻塞,等待Kernel返回。当Kernel接收到数据后,需要把数据拷贝的用户内存空间。然后Kernel(recvfrom)才返回,用户程序才能继续往下执行。

所以,Blocking IO在的数据准备和数据拷贝两个阶段,用户程序都是Blocking的。Socket编程提供的listen/send/recv等接口,都是Blocking模式的。

Blocking模式的主要优点时程序结构/流程清晰;缺点时每个线程同时只能给一个客户端提供服务,只能采用多线程(多进程)的方式来提供并发服务。采用多线程的方式有以下缺点:

每个线程(进程)占用一定的内存;

线程的创建删除的资源开销;

多线程调度会带来额外CPU开销;

对于多线程的问题,可以通过线程池来避免线程重复创建回收的开销,通过维护一定数量的线程,防止服务器资源耗尽等问题,通过请求排队处理,保持适当的并发服务等。但是对于成千上万的请求时,线程池模式还是会存在瓶颈。

非阻塞IO(Non-Blocking IO)

通过给Socket设置setblocking(False)把Socket设置为非阻塞模式,非阻塞模式下,通过recvform系统调用读取数据的流程如下:

15e097b44a6142cf0e4510e1614806d7.png

在Non-Blocking模式下,调用recvfrom时,如果Kernel还没有准备好数据,不会Block,而是直接返回Error。用户程序通过判断返回值,可以确定数据是否Ready。如果Kernel数据未准备好,用户程序可以再发起调用,或者趁这段时间做些其他操作,然后再回来调用,直到方法返回成功。

一旦用户再次调用recvfrom,并且Kernel数据已经Ready,就会把数据拷贝到用户内存空间,并返回。所以在Non-Blocking模式,用户程序需要通过不断调用Kernel(即轮询)来查询数据是否Ready,而且数据拷贝阶段还是Block的。

由于需要轮询,虽然用户程序在这段时间可以做其他操作,但是通常还是只是不断的执行轮询操作,轮询会大量占用CPU时间,而且期间去执行其他操作,会让程序执行逻辑混乱,所以被阻塞模式一般不被推荐使用,而是采用Kernel自带轮询的IO多路复用技术。

IO多路复用(IO Multiplexing)

IO多路复用,又叫事件驱动IO(Event Driver IO),底层使用了Kernel提供的select/poll/epoll(为什么有3种技术,也是从老到新一步步优化的过程)等系统调用。此技术的主要方法是在单个线程中,由select函数负责对多个Socket轮询,一旦其中有Socket数据Ready了,就会通知用户程序进行处理。

0017ac73b81f252bef5b93c980ee1b27.png

当调用了select后,用户程序会Block,同时Kernel监控此select负责的所有Socket,当有一个或多个Socket数据Ready事件发生时(事件驱动IO),select就会返回。用户程序再逐个调用Ready的Socket的recvfrom函数,把数据从Kernel拷贝到用户内存。

从流程上看,IO多路复用和Blocking IO区别不大,都会Block用户程序。IO多路复用的主要优点是一个线程可以Handle多个Socket。但如果IO数据接收后的处理任务是计算密集型业务时,由一个线程处理多个任务,性能上会无法胜任,客户端出现卡顿和延迟。

select模型的另外一个问题是虽然用户程序不在需要轮询了,但是Kernel还是需要去轮询Socket,消耗大量CPU,系统设置Socket的上限是1024。许多系统提供了更高效的接口,如Linux的epoll,BSD的kqueue等,但这些接口的主要问题是不能跨平台。

select

poll

epoll

实现年代

1984

1997

2002

底层数据结构

数组

链表

哈希表

连接数

1024

无限制

无限制,OS支持最大FD数

事件检测方法

遍历FD(File Descriptor)

遍历FD

IO就绪自动callback,检查就绪链表即可

时间复杂度

O(n)

O(n)

O(1)

FD处理

每次调用,需要把FD从用户内存拷贝到内核

每次调用,FD拷贝

epoll_ctl一次拷贝FD,无需重复拷贝

函数

select

poll

epoll_create创建句柄,epoll_ctl注册IO就绪监听事件,epoll_wait等待事件就绪

IO多路复用模式,对于单个Socket,是非阻塞的,以便在一个线程里同时处理多个Socket,但是select在处理轮询IO设备状态时,是阻塞的。

异步IO(Asynchronous I/O)

异步IO是Linux2.6内核引入的新功能,使用的不多,流程如下:

145eb855f788c24aef9adefd9719f689.png

用户程序发起aio_read调用后,程序立即返回。从Kernel的角度,流程如下:

当Kernel它收到aio_read之后,会立刻返回,不会对用户程序产生Block

Kernel等待数据准备完成,把数据拷贝到用户内存空间

Kernel给用户程序发送一个signal,告诉它read操作以完成

说实话,异步IO功能我也没用过,而且Linux底层还是使用epoll实现的,性能上并没有优势。Java著名NIO框架Netty就使用了IO多路复用,而非异步IO。

IO模型比较

直接上大神的图。

14a12cf2e0322de74e76c1e9c368776c.png

从上图可见,阻塞式IO和IO多路复用,特点还行比较明细的,本质上都是同步IO,就不展开了。

Non-Blocking虽然在数据准备阶段不阻塞了,但是用户程序还是需要轮询Check数据状态;而异步IO,发完请求后,用户程序就返回了,数据准备和拷贝,都由Kernel来完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值