当我们讨论网络编程中的 IO 模型时,我们需要澄清什么是 IO,以及为什么 IO 操作是程序开发的关键部分。
IO操作(输入输出操作)是计算机系统中的一项重要操作,用于数据的输入和输出,通常涉及计算机与外部设备(如硬盘、网卡、键盘、鼠标、打印机等)之间的数据传输和交互。
IO 操作可分为两种主要类型:
1 输入是指将数据从外部设备或数据源读取到计算机内存或程序中。 例如,从硬盘读取文件内容、从键盘接收用户输入、从网卡接收数据等。
2 输出是指将数据从计算机内存写入外部设备或数据目标。 例如,将数据写入硬盘上的文件、打印出文本、向网络发送数据等,都是输出操作。无论 IO 操作如何,都有两部分:
数据准备,将数据加载到内核缓存中。 (数据已加载到操作系统中)。
将数据从内核缓存加载到用户缓存中(从操作系统复制到应用中)。
因此,开发工作中涉及的网络读写、数据库操作、文件操作、日志打印等都可以归类为IO操作,IO操作的性能会受到多种因素的影响,包括硬件性能、操作系统优化、文件系统性能等。 网络IO是指计算机程序与网络之间数据交互的过程,在编码层面,我们可以简单地将其定义为套接字的套接字操作,例如套接字的创建和关闭,以及数据的发送和接收。
那么我们为什么要关心 IO 操作和多个 IO 模型的存在,因为 IO 操作的本质是与硬件数据交互,这个过程需要时间,返回的结果也需要等待,这也导致了我们常说的阻塞问题,你的程序需要停在那里等待, 所以在大多数程序开发场景中,IO操作通常是最耗时的操作之一,而且最有可能成为性能瓶颈的关键因素,尤其是一旦你的程序开始扩容,同样的程序,一旦需要按一个数量级进行处理,就会升级为一个复杂的问题;同理,网络IO之所以重要,反复提及,是因为在网络开发中,尤其是服务器开发中,不可能处理单个环节,而处理100个链路和处理100万个链路所面临的性能挑战是不一样的,这也是C10K问题的由来, 所以一些需要处理海量链路的服务应用,比如物联网服务、推送服务、 为了提高 IO 操作的性能,一些阻塞、非阻塞 IO、异步 IO 模型优化了 IO 对整个应用程序的性能影响,这是经常听到的 BIO 等 IO 模型, 当然,蔚来、AIO等,这只是解决上述问题的解决方案之一。
IO 根据其阻塞特性可分为阻塞 IO (BIO)、非阻塞 IO (NIO)、多路复用 IO 和异步 IO (AIO),非阻塞、同步和异步 IO。 每个 IO 都有自己的用例和优势,其中我们平时说的 NIO 已经包含了 IO 的复用,下图显示了每个 IO 与阻塞非阻塞、同步异步之间的关系。
其中,IO的阻塞、非阻塞、多路复用是需要轮询的同步IO,在真正的异步IO中,程序只需要等待一个完成信号的通知,也就是我们通常所说的异步机制。 因此,拉动子线程进行轮换或使用 select、poll 和 epoll 不是异步的。
我们上面已经解释了 IO 的定义和重要性,现在我们将结合 J**A** 的具体实现,看看不同 IO 模型的具体实现和特点。
bio(blocking i/o)
BIO 是最传统的阻塞 IO 模型,这意味着当线程执行 IO 操作时,它会等待操作完成。
每个 IO 操作都需要一个单独的线程进行处理,导致线程数量大幅增加,降低系统的并发性能。
适用于连接数量较少的一般应用场景。
public class bioserver }
public class bioserverhandler implements runnable @override public void run() catch (ioexception e)NIO 和 Multiplexing 是密切相关的概念,它们经常组合使用,尤其是在网络编程中,NIO 通常通过 multiplexing 机制实现非阻塞 IO。 多路复用允许线程同时监视多个通道的状态,多路复用机制在通道具有可读或可写数据时通知应用程序,使应用程序能够有效地处理多个通道的 IO 事件。
蔚来引入了通道和缓冲区的概念,允许单个线程管理多个通道,从而提高系统的并发性能。
通道和缓冲器:NIO 引入了通道和缓冲区的概念,允许一个线程管理多个通道。 通道表示与数据源(如文件或网络套接字)的连接,而缓冲区是用于读取或写入数据的缓冲区。 线程可以将数据从通道读取到缓冲区,或将数据从缓冲区写入通道,而无需等待数据准备就绪。
选择器:selector 是蔚来汽车的核心组件之一,它允许一个线程同时管理多个通道。 选择器可以检测多个通道是否准备好读取或写入数据,从而允许线程无阻塞地等待数据已准备就绪的通知。 这种机制称为事件驱动,允许线程同时监视多个通道上的事件,仅处理那些已经准备就绪的事件,而不是等待每个通道的数据准备就绪。
非阻塞呼叫:nio 中的通道和套接字通常可以配置为非阻塞模式。 在非阻塞模式下,当执行读写操作时,如果没有准备数据,则不会阻塞线程,而是立即返回一个状态码,指示没有可用的数据,以便线程可以继续处理其他通道,而不会被一次阻塞操作阻塞。
结合上述机制,蔚来允许单个线程同时处理多个连接,并在数据准备就绪时处理数据,而不是阻塞等待数据准备就绪。 这种低级别、事件驱动的方法可以提高应用程序系统的并发性能,特别是对于需要处理大量连接的场景。
public class nioserver selectionkeys.clear();catch (exception e)
public class nioserverhandler public void handle() socketchannel.configureblocking(false); socketchannel.register(selectionkey.selector(),selectionkey.op_read); if (selectionkey.isreadable())inputbuff.clear();int length = socketchannel.read(inputbuff); if (length == -1) inputbuff.flip();byte bytes = new byte[length]; system.arraycopy(inputbuff.array(),0, bytes, 0, length); system.err.println( bytesutils.tohexstring(bytes));socketchannel.register(selectionkey.selector(),selectionkey.op_write); selectionkey.selector().wakeup();唤醒选择器 } if (selectionkey..)iswritable())string message = "hello, client. " + uuid.randomuuid();system.err.println(message); outputbuff.put(message.getbytes(standardcharsets.utf_8));outputbuff.flip();socketchannel.write(outputbuff); socketchannel.register(selectionkey.selector(),selectionkey.op_read); selectionkey.selector().wakeup();唤醒选择器 } catch(异常 e)。AIO(异步 IO),也称为异步 IO,是一种用于处理输入和输出操作的编程模型。 它与传统的BIO和NIO模型有一些显着的区别。 它不需要由 BIO 和 NIO 轮询来检查数据是否准备就绪,而是由操作系统完成的。 当数据准备就绪时,操作系统会通知应用程序并在函数中对其进行处理。
AIO 使用三个核心组件:AsynchronousChannel、CompletionHandler 和 .
asynchronousserversocketchannel。其中,asynchronouschannel 是读写数据的通道,completionHandler 是 io 操作完成时的 ** 方法,asynchronousserversocketchannel 是监听客户端连接请求的异步服务器端 socket 通道。
以下是AIO的主要特点:
异步操作:AIO最重要的特点是它以异步方式运行。 在 AIO 模型中,应用程序启动 IO 操作后,不需要等待操作完成,而是可以继续执行其他任务。 操作完成后,操作系统会通知应用程序,该模型不会阻止应用程序的执行。
机制:AIO 使用 ** 机制来处理 i o 完成事件。 当应用程序启动异步操作时,它需要提供一个函数或对象,用于在操作完成时处理事件通知。 这使得 AIO 编程更加由事件驱动。
提高并发性能:AIO 能够在高并发环境中提供更好的性能,因为它在蔚来能够在单个线程中管理多个 IO 操作的基础上实现异步,因此可以更好地处理大量连接。
复杂性: AIO 编程比较复杂,因为它同时涉及状态管理和状态管理。 编写和维护 AIO 可能需要更多的工作,但可以提供更好的性能和响应能力。
综上所述,AIO是一种适用于高并发、低时延要求的应用的编程模型。 它的异步特性和事件驱动的方法使应用程序能够更好地利用系统资源,从而提供更好的性能和响应能力。 然而,AIO 通常比传统的 BIO 或 NIO 实现更复杂,因此设计和编码的复杂性会相对较高。
public class aioserver @override public void failed(throwable exc, void attachment) thread.currentthread().join();
public class aioserverhandler implements runnable @override public void run() catch (exception e) return; }inputbuff.flip();byte data = new byte[bytesread]; inputbuff.get(data); string message = new string(data); system.out.println("received message: " + message); outputbuff.clear();outputbuff.put(message.getbytes(standardcharsets.utf_8));outputbuff.flip();clientchannel.write(outputbuff, null, new completionhandler() override public void failed(throwable exc, void attachment) inputbuff.clear();clientchannel.read(inputbuff, null, this); override public void failed(throwable exce, void attachment) catch (exception e)了解不同IO模型的特点和具体实现,是我们设计和开发大规模应用的基础,这有助于我们根据不同的应用场景和性能要求,选择最合适的模型,设计架构同时,Mina、Netty等一些常用的网络服务框架也基于高性能IO模型进行了深度封装和扩展,通过结合这些框架的源码,我们可以加深对网络IO模型在实际开发中的应用的理解。 今后,我将继续丰富和完善本章的内容,解释一个完整的 Web 应用程序需要具备的各种功能并实现它们。
作者: **njoy
*:cnblogs.com/***njoy/p/17910875.html