NIO、Reactor模式与直接内存

news2024/11/13 16:17:13

1.NIO

NIO有三大核心组件:Selector选择器、Channel管道、buffer缓冲区。、

1.1Selector

Selector的英文含义是“选择器”,也可以称为为“轮询代理器”、“事件订阅器”、“channel容器管理机”都行。

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器(Selectors),然后使用一个单独的线程来操作这个选择器,进而“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

应用程序将向Selector对象注册需要它关注的Channel,以及具体的某一个Channel会对哪些IO事件感兴趣。Selector中也会维护一个“已经注册的Channel”的容器。

1.2Channels

通道,被建立的一个应用程序和操作系统交互事件、传递内容的渠道(注意是连接到操作系统)。那么既然是和操作系统进行内容的传递,那么说明应用程序可以通过通道读取数据,也可以通过通道向操作系统写数据,而且可以同时进行读写。

通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

所有被Selector(选择器)注册的通道,只能是继承了SelectableChannel类的子类;ServerSocketChannel:应用服务器程序的监听通道。只有通过这个通道,应用程序才能向操作系统注册支持“多路复用IO”的端口监听。同时支持UDP协议和TCP协议。

ScoketChannel:TCP Socket套接字的监听通道,一个Socket套接字对应了一个客户端IP:端口 到 服务器IP:端口的通信连接。

1.3buffer缓冲区

JDK NIO是面向缓冲的。Buffer就是这个缓冲,用于和NIO通道进行交互。数据是从通道读入缓冲区,从缓冲区写入到通道中的。以写为例,应用程序都是将数据写入缓冲,再通过通道把缓冲的数据发送出去,读也是一样,数据总是先从通道读到缓冲,应用程序再读缓冲的数据。

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存(其实就是数组)。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。

2.单线程Reactor模式 

1)服务器端的Reactor是一个线程对象,该线程会启动事件循环,并使用Selector(选择器)来实现IO的多路复用。注册一个Acceptor事件处理器到Reactor中,Acceptor事件处理器所关注的事件是ACCEPT事件,这样Reactor会监听客户端向服务器端发起的连接请求事件(ACCEPT事件)。

2)当客户端向服务器端发起一个连接请求,Reactor监听到了该ACCEPT事件的发生,就会将该ACCEPT事件派发给相应的Acceptor处理器来进行处理。Acceptor处理器通过accept()方法得到与这个客户端对应的连接(SocketChannel),然后将该连接所关注的READ事件以及对应的READ事件处理器注册到Reactor中,这样一来Reactor就会监听该连接的READ事件了。

3) 当Reactor监听到有读或者写事件发生时,将相关的事件派发给对应的处理器进行处理。

比如,读处理器会通过SocketChannel的read()方法读取数据,此时read()操作可以直接读取到数据,而不会堵塞与等待可读的数据到来。

4)每当处理完所有就绪的、感兴趣的I/O事件后,Reactor线程会再次执行select()阻塞等待新的事件就绪,并将其分派给对应处理器进行处理。

注意,Reactor的单线程模式的单线程主要是针对于I/O操作而言,也就是所有的I/O的accept()、read()、write()以及connect()操作都在一个线程上完成的。

但在目前的单线程Reactor模式中,不仅I/O操作在该Reactor线程上,连非I/O的业务操作也在该线程上进行处理了,这可能会大大延迟I/O请求的响应。所以我们应该将非I/O的业务逻辑操作从Reactor线程上卸载,以此来加速Reactor线程对I/O请求的响应。

3.单线程Reactor和工作者线程池

与单线程Reactor模式不同的是,添加了一个工作者线程池,非I/O操作从Reactor线程中移出 转交给工作者线程池来执行。这样能够提高Reactor线程的I/O响应,不至于因为一些耗时的业务逻辑而延迟对后面I/O请求的处理。

使用线程池的优势:

1)通过重用现有的线程而不是创建新线程,可以在处理多个请求时分摊在线程创建和销毁过程产生的巨大开销。

2)当请求到达时,工作线程通常已经存在,因此不会由于等待创建线程而延迟任务的执行,从而提高了响应性。

3)通过适当调整线程池的大小,可以创建足够多的线程以便使处理器保持忙碌状态。同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或失败。

在改进的版本中,所以的I/O操作依旧由一个Reactor来完成,包括I/O的accept()、read()、write()以及connect()操作。

对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发或大数据量的应用场景却不合适,主要原因如下:

1)一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的读取和发送;

2)当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈。

4.多线程主从Reactor模式

Reactor线程池中的每一Reactor线程都会有自己的Selector、线程和分发的事件循环逻辑

mainReactor可以只有一个,但subReactor一般会有多个。mainReactor线程主要负责接收客户端的连接请求,然后将接收到的SocketChannel传递给subReactor,由subReactor来完成和客户端的通信

流程:

1) 注册一个Acceptor事件处理器到mainReactor中,Acceptor事件处理器所关注的事件是ACCEPT事件,这样mainReactor会监听客户端向服务器端发起的连接请求事件(ACCEPT事件)。启动mainReactor的事件循环。

2)客户端向服务器端发起一个连接请求,mainReactor监听到了该ACCEPT事件并将该ACCEPT事件派发给Acceptor处理器来进行处理。Acceptor处理器通过accept()方法得到与这个客户端对应的连接(SocketChannel),然后将这个SocketChannel传递给subReactor线程池

3)subReactor线程池分配一个subReactor线程给这个SocketChannel,即,将SocketChannel关注的READ事件以及对应的READ事件处理器注册到subReactor线程中。当然你也注册WRITE事件以及WRITE事件处理器到subReactor线程中以完成I/O写操作。Reactor线程池中的每一Reactor线程都会有自己的Selector、线程和分发的循环逻辑。

4) 当有I/O事件就绪时,相关的subReactor就将事件派发给响应的处理器处理。注意,这里subReactor线程只负责完成I/O的read()操作,在读取到数据后将业务逻辑的处理放入到线程池中完成,若完成业务逻辑后需要返回数据给客户端,则相关的I/O的write操作还是会被提交回subReactor线程来完成。

注意,所以的I/O操作(包括,I/O的accept()、read()、write()以及connect()操作)依旧还是在Reactor线程(mainReactor线程 或 subReactor线程)中完成的。Thread Pool(线程池)仅用来处理非I/O操作的逻辑。

多Reactor线程模式将“接受客户端的连接请求”和“与该客户端的通信”分在了两个Reactor线程来完成。mainReactor完成接收客户端连接请求的操作,它不负责与客户端的通信,而是将建立好的连接转交给subReactor线程来完成与客户端的通信,这样一来就不会因为read()数据量太大而导致后面的客户端连接请求得不到即时处理的情况。并且多Reactor线程模式在海量的客户端并发请求的情况下,还可以通过实现subReactor线程池来将海量的连接分发给多个subReactor线程,在多核的操作系统中这能大大提升应用的负载和吞吐量。

 

补充-和观察者模式的区别

观察者模式: 也可以称为为 发布-订阅 模式,主要适用于多个对象依赖某一个对象的状态,当某对象状态发生改变时,要通知其他依赖对象做出更新。是一种一对多的关系。

当然,如果依赖的对象只有一个时,也是一种特殊的一对一关系。通常,观察者模式适用于消息事件处理,监听者 监听到事件时通知事件处理者对事件进行处理。

Reactor模式: reactor模式,即反应器模式,是一种高效的异步IO模式,特征是回调,当IO完成时,回调对应的函数进行处理。这种模式并非是真正的异步,而是运用了异步的思想,当IO事件触发时,通知应用程序作出IO处理。模式本身并不调用系统的异步IO函数。

reactor模式与观察者模式有点像。不过,观察者模式与单个事件源关联,而反应器模式则与多个事件源关联 。当一个主体发生改变时,所有依属体都得到通知。

4.直接内存(堆外内存)

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError 异常出现。

NIO可以使用Native 函数库直接分配堆外内存,然后通过一个存储在Java 堆里面的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java 堆和Native 堆中来回复制数据。

在IO读写上,如果是使用堆外内存,JDK会先创建一个DirectBuffer,再去执行真正的写操作。这是因为,当我们把一个地址通过JNI传递给底层的C库的时候,有一个基本的要求,就是这个地址上的内容不能失效。然而,在GC管理下的对象是会在Java堆中移动的。也就是说,有可能我把一个地址传给底层的write,但是这段内存却因为GC整理内存而失效了。所以必须要把待发送的数据放到一个GC管不着的地方。这就是调用native方法之前,数据—定要在堆外内存的原因。

可见,站在网络通信的角度DirectBuffer并没有节省什么内存拷贝,只是Java网络通信里因为HeapBuffer必须多做一次拷贝,使用DirectBuffer就会少一次内存拷贝。相比没有使用堆内存的Java程序,使用直接内存的Java程序当然更快一点。

从垃圾回收的角度而言,直接内存不受 GC(新生代的 Minor GC) 影响,只有当执行老年代的 FullGC 时候才会顺便回收直接内存,整理内存的压力也比数据放到HeapBuffer要小。

直接内存的优点和缺点:

堆外内存相比于堆内内存有几个优势:

1)减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到)

2)加快了复制的速度。因为堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。

缺点:

1)堆外内存难以控制,如果内存泄漏,那么很难排查;

2)堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2109192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙MPChart图表自定义(四)短刻度线

对于图表中的x轴效果,我们有时想要实现如图所示的特定刻度线。若需绘制x轴的短刻度线,我们可以利用现有资源,将原本的网格线稍作修改,只需绘制一条简洁的短线即可达到目的。 具体的方法就是写一个类MyXAxisRender继承自XAxisRend…

iOS——runLoop

什么是runloop RunLoop实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行相应的处理逻辑。线程执行了这个函数后,就会处于这个函数内部的循环中,直到循环结束,函数返回。 RunLoo…

【转载】golang内存分配

Go 的分配采用了类似 tcmalloc 的结构.特点: 使用一小块一小块的连续内存页, 进行分配某个范围大小的内存需求. 比如某个连续 8KB 专门用于分配 17-24 字节,以此减少内存碎片. 线程拥有一定的 cache, 可用于无锁分配. 同时 Go 对于 GC 后回收的内存页, 并不是马上归还给操作系…

Android13 Hotseat客制化--Hotseat修改布局、支持滑动、去掉开机弹动效果、禁止创建文件夹

需求如题,实现效果如下 : 固定Hotseat的padding位置、固定高度 step1 在FeatureFlags.java中添加flag,以兼容原生态代码 public static final boolean STATIC_HOTSEAT_PADDING true;//hotseat area fixed step2:在dimens.xml中添加padding值和高度值…

信息系统安全保障

关注这个证书的其他相关笔记:NISP 一级 —— 考证笔记合集-CSDN博客 0x01:信息系统 信息系统是具有集成性的系统,每一个组织中信息流动的综合构成一个信息系统。信息系统是根据一定的需要进行输入、系统控制、数据处理、数据存储与输出等活动…

职场关系课:职场上的基本原则(安全原则、进步原则、收益原则、逃生舱原则)

文章目录 引言安全原则进步原则收益原则逃生舱原则引言 职场上的王者,身体里都应该有三个灵魂: 一个文臣,谨小慎微,考虑风险; 一个武将,积极努力,谋求胜利; 一个商人,精打细算,心中有数。 安全原则 工作安全:保住自己的工作和位置信用安全:保住个人的信用,如果领…

《征服数据结构》差分数组

摘要: 1,差分数组的介绍 2,二维差分数组的介绍 1,差分数组的介绍 差分数组主要是操作区间的,关于区间操作的数据结构比较多,除了前面讲的《稀疏表》,还有树状数组,线段树&#xff0c…

高德地图SDK Android版开发 10 InfoWindow

高德地图SDK Android版开发 10 InfoWindow 前言相关类和方法默认样式Marker类AMap类AMap.OnInfoWindowClickListener 接口 自定义样式(视图)AMap 类AMap.ImageInfoWindowAdapter 接口 自定义样式(Image)AMap.ImageInfoWindowAdapter 接口 示例界面布局MapInfoWindow类常量成员变…

215篇【大模型医疗】论文合集(附PDF)

ChatGPT的横空出世引发了新一轮生成式大模型热潮,作为最新技术的"试验场",医疗也成为众多大模型的热门首选。 我整理了215篇医疗和大模型的论文,供大家学习和参考。 领215篇医疗和大模型论文

欧拉下搭建第三方软件仓库—docker

1.创建新的文件内容 切换目录到etc底下的yum.repos.d目录,创建docker-ce.repo文件 [rootlocalhost yum.repos.d]# cd /etc/yum.repos.d/ [rootlocalhost yum.repos.d]# vim docker-ce.repo 编辑文件,使用阿里源镜像源,镜像源在编辑中需要单独复制 h…

vue3的学习(2)

属性绑定 1.将一个容器中的class和id使用vue用法赋上具体的值,这样就可以动态的给容器添加上自己想要给其添加的class或者id或者title。 2.关键语法,在容器中的class或者id或者title前面加上 "v-bind:",当加上"v-bind关键语…

计算机网络基础 - 应用层(2)

计算机网络基础 应用层FTP 与 EMail文件传输协议 FTP电子邮件 EMail主要组成部分SMTP概述SMTP 与 HTTP1.1 邮件报文格式报文格式多媒体扩展 MIME 邮件访问协议概述POP3IMAP DNS概述域名结构工作机理集中式设计分布式、层次数据库根 DNS 服务器顶级域 DNS 服务器权威 DNS 服务器…

公司的企业画册如何制作?

企业画册是公司形象和产品服务展示的重要载体,一个制作精良的企业画册不仅能展示公司的实力,也能提升客户对公司专业度的认可。以下是制作企业画册的步骤和要点,帮助你的公司画册既美观又实用。 1.要制作电子杂志,首先需要选择一款适合自己的…

OpenCV结构分析与形状描述符(7)计算轮廓的面积的函数contourArea()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 计算轮廓的面积。 该函数计算轮廓的面积。与 moments 类似,面积是使用格林公式计算的。因此,返回的面积与你使用 drawCo…

Mysql中的隐式COMMIT以及Savepoints的作用以及MySQL的Innodb分空间存储、设计优化、索引等几个小知识点整理

一、Mysql中的隐式COMMIT以及Savepoints的作用 Mysql默认是自动提交的,如果要开启使用事务,首先要关闭自动提交后START TRANSACTION 或者 BEGIN 来开始一个事务,使用ROLLBACK/COMMIT来结束一个事务。但即使如此,也并不是所有的操作…

零知识证明在BSV网络上的应用

​​发表时间:2023年6月15日 2024年7月19日,BSV区块链主网上成功通过使用零知识证明验证了一笔交易。 零知识证明是一种技术,它允许一方(证明者)在不透露任何秘密的情况下,向另一方(验证者&…

TP-link-路由器上网设置(已有路由器再连接新的网线)

一、192.168.0.1进入管理界面,比如密码:D804D804。 二、这是设置连接账户和密码(比如账户:TP-LINK_F56C 比如密码:D804D804)登录后台管理、移动设备连接。比较固定。 三、 有的网络是分:&#…

JS面试真题 part1

JS面试真题 part1 1、说说JavaScript中的数据类型,储存上的差别2、说说你了解的js数据结构3、DOM常见的操作有哪些4、说说你对BOM的理解,常见的BOM对象你了解哪些5、 和 区别,分别在什么情况使用 1、说说JavaScript中的数据类型,…

K8s的Pv和Pvc就是为了pod数据持久化

一、 1.pv(persistent volume):是k8s虚拟化的存储资源,实际上就是存储,列如本地的硬盘、网络文件系统(Nfs)、lvm、RAID、云存储。 2.pvc:pod对存储资源的请求,定义了需…

MyBatis:解决数据库字段和Java对象字段不匹配问题及占位符问题

MyBatis:解决数据库字段和Java对象字段不匹配问题及占位符问题 文章目录 MyBatis:解决数据库字段和Java对象字段不匹配问题及占位符问题一、数据库字段和Java对象字段不匹配问题1、问题描述2、解决方案2.1、方案12.2、方案22.3、方案3 二、占位符问题1、…