资讯专栏INFORMATION COLUMN

NIO网络相关基础知识

1fe1se / 2508人阅读

摘要:操作系统是能够获取到事件操作完成的事件,基于回调函数机制和操作系统的操作控制实现事件检测机制。

前面的文章NIO基础知识介绍了Java NIO的一些基本的类及功能说明,Java NIO是用来替换java 传统IO的,NIO的一些新的特性在网络交互方面会更加的明显。

Java 传统IO的弊端

    “基于JVM来实现每个通道的轮询检查通道状态的方法是可行的,但仍然是有问题的,检查每个通道是否就绪是至少需要一次系统调用,执行的代价是非常昂贵的。同时这种检查不是原子的。列表中的每个通道在检查之后状态变成就绪,但需要等到下一次轮询之前JVM是无法感知的。最糟糕的是,JVM除了不断遍历列表之外将别无选择。JVM无法在某通道就绪时直接得到通知。
    这就是为什么监控多个socket连接的传统的java方案是:为每个socket创建一个线程并使线程可以再read()调用中阻塞,直到数据可用。这实际上将每个阻塞在对应socket上的线程当做了socket事件监控器,并将JVM的线程调度当做了事件通知。但是线程的阻塞和JVM的线程调用都是为了这种目的而设计的。当线程数量增长失控时,JVM为了管理这些线程,导致程序性能降低。”--摘自《JAVA NIO》
上述两段话摘自《JAVA NIO》第四章-选择器中,主要阐述了java 传统NIO的基本实现和弊端。通过上述文字我们也知道了,提高传统IO的性能可以从两方面入手:1.减少线程数量 2.实时获取IO事件

I/O模型

传统I/O为了能实时获取I/O事件,所以才会给每个socket连接分配一个线程用于监控socket事件,因为如何获取I/O事件通知是关键。因此调整JVM的I/O模型是提高I/O性能关键。
我们来了解下I/O模型的类型:

单线程阻塞I/O模型

只能同时处理一个客户端请求,并且在I/O操作上是阻塞的,服务端线程会一直等待I/O操作完成,不会做其他的事情。服务端读取客户端数据时要等待客户端发送数据并且操作系统内核复制到用户进程中之后才解除阻塞状态;服务端写数据回客户端是要等待用户进程将数据写入内核并发送到客户端后才解除阻塞状态。单线程阻塞I/O模型无法同时处理多个连接,只能串行处理连接。整个运行过程都只有一个线程,服务端系统资源消耗较小,但并发能力低。

多线程阻塞I/O模型

利用多线程机制为每个客户端分配一个线程,每个线程执行读取客户端的数据或数据成功写入客户端后才解除阻塞状态。支持多个客户端并发响应,处理能力得到提高。系统资源消耗较大,多线程之间会产生线程切换成本,同时为了实现线程安全引入线程同步机制导成程序结构复杂。

单线程非阻塞I/O模型

在调用读写接口后立即返回,而不会进入阻塞状态;基于事件检测机制获取到事件发生,进行对应事件的I/O操作。
事件检测方式:

应用程序遍历套接字的事件检测

服务端程序会保存一个socket套接字连接列表,应用层线程对socket套接字列表轮询尝试读取或写入。如果尝试失败,则在下一个循环再继续尝试。查询每个socket套接字都至少需要一次系统调用,而且无法立即获取socket套接字状态。同时应用程序除了要遍历socket套接字列表之外,还需要处理数据的拼接,实际会占用较多的CPU资源

内核遍历套接字的事件检测

将遍历socket列表的工作交给操作系统内核,应用层向发去操作系统内核请求读写列表。操作系统内核会遍历所有套接字并生成对应的可读列表readList和可写列表writeList返回给应用程序。应用程序获取到具体socket,对每个socket进行相应的I/O操作。
应用程序向内核请求读写列表,内核遍历所有的套接字并生成对应的可读列表readList和可写列表writeList。readList标明了每个套接字是否可读,例如套接字2、套接字3的值为1表示可读,套接字1的值为0表示不可读;同样,writeList表示套接字是否可写。

内核基于事件回调的事件检测

遍历套接字列表是个效率比较低的方式,无论是在内核层还是在应用层。操作系统是能够获取到I/O事件操作完成的事件,基于回调函数机制和操作系统的I/O操作控制实现事件检测机制。

基于回调机制的完全套接字可读可写列表

用可读列表readList和可写列表writeList标记读写事件,套接字的数量与readList和writeList两个列表的长度一样。readList元素为1表示可读,writeList元素为1表示可写。当客户端发送数据到服务端,内核完成从网卡复制数据调用回调函数将对应套接字readList中的元素置为1;若套接字已经做好写操作准备,内核会将套接字对应writeList中的元素置为1。应用程序发送请求读、写事件列表,内核会把包含readList和writeList的事件列表返回给应用程序,应用程序分别遍历列表,进而记性读写操作。

基于回调机制的部分套接字事件列表

如果套接字数量变大,事件列表传输也是不小的开销。可以让应用层告诉内核每个套接字感兴趣的事件,当客户端发送数据过来时,内核完成从网卡复制后即调用回调函数将套接字相关信息封装成可读事件event放到事件列表中;同样,内核发现网卡可写时会将套接字相关信息封装成可写事件event添加到事件列表中,事件列表中只有处于ready状态的那部分套接字对应的事件。java NIO即是采用这种事件检测机制,在客户端连接大多数处于活跃的情况下,服务端只用一个线程一直循环处理这些连接,很好地利用了I/O操作的阻塞时间,提高了线程执行效率。

多线程非阻塞I/O模型

单线程非阻塞I/O已经大大提高了机器的执行效率,在多核机器上为了更好的提高CPU的使用率可以引入多线程。引入reactor线程模型,每个线程处理不同类型的事件连接。

NIO网络通信

java NIO性能较java传统IO有较大提升主要是在网络交互中,通过Selector来管理客户端的连接,应用程序将客户端socket连接注册到Selector对象上。当客户端socket连接有数据准备就绪或连接准备好写操作时,应用程序通过selector获取到对应socket通道(Channel),应用程序通过Channel对socket进行读写操作。
NIO中服务端一个线程可以同时与多个客户端连接进行读写交互;而传统IO则是服务端的一个线程只能同时处理一个客户端,I/O操作时,线程处于阻塞状态。
Selector是SelectableChannel的多路复用选择器,用于监控SelectableChannel的IO状况的。在Selector定义为SelectableChannel定义了四种事件类型:
SelectionKey.OP_READ        读事件
SelectionKey.OP_WRITE       写事件
SelectionKey.OP_CONNECT    接收到客户端的连接事件
SelectionKey.OP_ACCEPT      接收到客户端的请求事件
多个事件之间用“|”连接
当服务端线程注册事件发生时,操作系统内核会通过回调函数将该事件放到读写事件列表中。JVM通过Selector的select方法会获取到已经注册到Selector对象上的事件列表。若没有事件发生select方法会一直阻塞知道有注册的时间发生才会把发生的时间返回给Selector对象。

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

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

相关文章

  • JDK10都发布了,nio你了解多少?

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

    YFan 评论0 收藏0
  • 准备好了再通知我:网络NIO

    摘要:当的数据准备好时,就会接到通知,得到那些已经准备好的数据。当与客户端连接的数据没有准备好时,会处于等待状态不过幸好,用于管理的线程数是极少量的,而一旦有任何一个准备好了数据,就能立即得到通知,获取数据进行处理。 java NIO是NEW IO的简称,它是一种可以替代javaIO的一套新的IO机制。它提供了一套不同java标准IO的操作机制。严格来说,NIO与并发并无直接关系。但是,使用...

    gougoujiang 评论0 收藏0
  • I/O模型和Java NIO源码分析

    摘要:最近在学习网络编程和相关的知识,了解到是模式的网络框架,但是提供了不同的来支持不同模式的网络通信处理,包括同步异步阻塞和非阻塞。因为的版本使用的的模式,而则希望使用模式,而且版本没有将的部分配置项暴露出来,比如说和。   最近在学习Java网络编程和Netty相关的知识,了解到Netty是NIO模式的网络框架,但是提供了不同的Channel来支持不同模式的网络通信处理,包括同步、异步、...

    yuanxin 评论0 收藏0
  • 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

    摘要:的选择器允许单个线程监视多个输入通道。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动通常不会。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之...

    Meils 评论0 收藏0
  • 关于Java IO与NIO知识都在这里

    摘要:从通道进行数据写入创建一个缓冲区,填充数据,并要求通道写入数据。三之通道主要内容通道介绍通常来说中的所有都是从通道开始的。从中选择选择器维护注册过的通道的集合,并且这种注册关系都被封装在当中停止选择的方法方法和方法。 由于内容比较多,我下面放的一部分是我更新在我的微信公众号上的链接,微信排版比较好看,更加利于阅读。每一篇文章下面我都把文章的主要内容给列出来了,便于大家学习与回顾。 Ja...

    Riddler 评论0 收藏0

发表评论

0条评论

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