Netty 的网络 IO 模型 - Reactor
lipiwang 2024-11-26 06:06 8 浏览 0 评论
Netty 怎么切换三种 IO 模式
什么是经典的三种 IO 模式
BIO ,阻塞 IO 模型(JDK 1.4 之前)
NIO ,非阻塞 IO 模型(JDK 1.4(2002 年, java.nio 包))
AIO ,异步 IO 模型(JDK 1.7(2011 年)
网络通信 IO 模型
阻塞和非阻塞
数据就绪前要不要等待?
- 阻塞:没有数据传过来时,读操作会阻塞到直到有数据;缓冲区满时,写操作也会阻塞。
- 非阻塞:遇到上面的情况都是直接返回。
同步和异步
数据就绪后,操作由谁来完成?
- 同步:数据就绪后自己去读。
- 异步:数据就绪后再自己回调给应用程序。
Netty 对三种 IO 模型的支持
Netty 对三种 IO 模型的支持 表格汇总
为什么 Netty 仅支持 NIO 了?
Netty 仅仅支持 NIO 的原因
- 为什么不建议( Deprecate )阻塞 IO( BIO/OIO )?连接数高的情况下,也就是高并发情况下,阻塞 -> 耗资源、效率低。
- 为什么删掉已经做好的 AIO 支持?Windows 的 AIO 实现成熟,但是很少用来做服务器;Linux 常常用来做服务器,但是 AIO 的实现不成熟;Linux 下 AIO 相比较 NIO 的性能提升不明显。
为什么 Netty 有多种 NIO 实现
Netty 对 NIO 的多种实现
通用的 NIO 实现(Common)在 Linux 下面也是使用 epoll 函数,为什么要单独实现?
- 实现得更好
- Netty 暴露了更多的可控参数,例如:JDK 的 NIO 是水平触发;Netty 是边缘触发(默认)和水平触发可以切换。
- Netty 的实现垃圾回收更少、性能更好。
细说八股 | NIO的水平触发和边缘触发到底有什么区别?
NIO 一定优于 BIO 么
- 不一定。
- BIO 代码简单(相对于 NIO )。适用于特定场景:连接数少,并发度低,此时 BIO 性能不输 NIO 。
源码解读 Netty 怎么切换 IO 模型
问题一:怎么切换 IO 模型
如上图,将前缀 Nio 修改为 Oio 即切换成功,非常简单。
切换 IO 模型的原理是什么?
- 以 channel 方法为例:
- channel 方法源码为:
- 很明显 ReflectiveChannelFactory 从命名上来看是一个 Channel 的反射工厂;
- 继续跟进去:
- 这个方法的逻辑就是获取参数的无参构造器,然后再赋值给自己的一个构造器属性。
- 看上去好像没什么,此时可以注意下面的一个方法:
- 这个方法的逻辑就是构造一个实例,可以看出这个方法是接口的方法,那么是谁的接口呢?
- 继续跟进去:
- ChannelFactory ,从命名上看,是一个构造 Channel 的工厂,刚刚的方法实现也证明了这一点,那么谁调用了这个方法呢?
- 继续跟进去:
- 我们发现是 AbstractBootstrap 抽象类的 initAndRegister() 方法,并且这个方法还是用 final 修饰的,意味着这是一个模板方法,子类不可更改, initAndRegister() 方法里面执行了 channel = channelFactory.newChannel(); 构造了一个 Channel ;
- AbstractBootstrap 看上去有点陌生,我们看看它的子类我们有哪些呢:
- 好像一切变得熟悉起来了,也就是说是服务端或者客户端启动的时候构建了 Channel ,并且这个 Channel 的类型是根据你传入的类型进行构造的。 总结就是:泛型 + 反射 + 工厂实现 IO 模型切换。
Netty 如何支持三种 Reactor 模型
什么是 Reactor 及三种版本
BIO | NIO | AIO |
Thread-Per-Connection | Reactor | Proactor |
Reactor 介绍
Reactor 是一种开发模型,模型的核心流程为: 注册事件 -> 扫描事件是否发生 -> 事件发生后做出相应的处理 。
- OP_ACCEPT :请求操作; OP_CONNECT :连接操作; OP_WRITE :写数据操作; OP_READ :读数据操作;
Thread-Per-Connection 模型
(1)核心思路
(2)代码实现( BIO )
Reactor 模型 V1:单线程
对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,主要原因如下:
(1)一个 NIO 线程同时处理成百上千的链路,性能上无法支撑,即便 NIO 线程的 CPU 负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;
(2)当 NIO 线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了 NIO 线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;
(3)可靠性问题:一旦 NIO 线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。
为了解决这些问题,演进出了 Reactor 多线程模型。
Reactor 模型 V2:多线程
在绝大多数场景下, Reactor 多线程模型都可以满足性能需求;但是,在极个别特殊场景中,一个 NIO 线程负责监听和处理所有的客户端连接可能会存在性能问题。
例如并发百万客户端连接,或者服务端需要对客户端握手进行安全认证,但是认证本身非常损耗性能。在这类场景下,单独一个 Acceptor 线程可能会存在性能不足问题,为了解决性能问题,产生了 主从 Reactor 多线程模型。
Reactor 模型 V3:主从多线程
mainReactor 只负责处理连接,至于真正的事件处理则交给 subReactor 线程,这样分工的好处就是各不干扰,并且 main 那里。
如何在 Netty 中使用 Reactor 模型
Netty 中使用 Reactor 模型相关 API
源码解读 Netty 对 Reactor 模式支持的常见疑问
Netty 是如何支持主从 Reactor 模型的?
主要思路就是,当接收连接的时候会建立一个 ServerSocketChannel 并注册到 mainReactor 中,同时 ServerSocketChannel 会创建一个 Channel 注册到 subReactor 中,这样就完成了主从 Recator 的绑定关系。
为什么说 Netty 中的 main reactor 大多不能用到一个线程组,只能用到线程组里面的一个线程?
端口号只会绑定一次。
Netty 给 Channel 分配 NioEventLoop 的规则是什么?
两种规则:
(1)取模
通过一个 AtomicInteger 原子变量进行自增,然后模除 NioEventLoop 的个数,注意这里 AtomicInteger 原子变量要取绝对值,因为在自增到一定情况下是会出现负数的。
(2)按位与 &
如果 NioEventLoop 的个数为 2 的幂次方,就可以通过 & 的方式来进行选择,就和 HashMap 元素的哈希值和索引的映射方法是一样的。
通用模式的 NIO 实现多路复用器是如何跨平台的?
(1) NioEventLoop 的构造器
(2)进入 SelectorProvider.provider() 方法:
- loadProviderFromProperty() 方法的逻辑就是根据你的配置文件中的 java.nio.channels.spi.SelectorProvider 属性来加载并选择一个复用器,如果获取不到就返回 false ,一般情况下获取不到,返回 false ;
- loadProviderAsService() 方法的逻辑就是根据你的 META-INF 文件夹下的配置文件来加载并选择一个复用器,如果获取不到就返回 false ,一般情况下获取不到,返回 false ;
- 那么真正执行的就是: sun.nio.ch.DefaultSelectorProvider.create() 方法了
(3)进入 sun.nio.ch.DefaultSelectorProvider.create() 方法
我们可以发现它返回了一个 Window 的多路复用器,这就是说明这是和 JDK 的版本有关的,因为我的 JDK 是 Windows 版本的,我们可以看一下 Mac 版本的 JDK ,这个方法它会返回什么?
源码地址
所以这就是跨平台的思路,通过调用 nio 的方法,因为 nio 在不同的 JDK 版本都有不同的实现,这需要调用 nio 的方法就好了。
原文链接:juejin.cn/post/7119017102593228808?utm_source=tuicool&utm_medium=referral
相关推荐
- linux实例之设置时区的方式有哪些
-
linux系统下的时间管理是一个复杂但精细的功能,而时区又是时间管理非常重要的一个辅助功能。时区解决了本地时间和UTC时间的差异,从而确保了linux系统下时间戳和时间的准确性和一致性。比如文件的时间...
- Linux set命令用法(linux cp命令的用法)
-
Linux中的set命令用于设置或显示系统环境变量。1.设置环境变量:-setVAR=value:设置环境变量VAR的值为value。-exportVAR:将已设置的环境变量VAR导出,使其...
- python环境怎么搭建?小白看完就会!简简单单
-
很多小伙伴安装了python不会搭建环境,看完这个你就会了Python可应用于多平台包括Linux和MacOSX。你可以通过终端窗口输入"python"命令来查看本地是否...
- Linux环境下如何设置多个交叉编译工具链?
-
常见的Linux操作系统都可以通过包管理器安装交叉编译工具链,比如Ubuntu环境下使用如下命令安装gcc交叉编译器:sudoapt-getinstallgcc-arm-linux-gnueab...
- JMeter环境变量配置技巧与注意事项
-
通过给JMeter配置环境变量,可以快捷的打开JMeter:打开终端。执行jmeter。配置环境变量的方法如下。Mac和Linux系统在~/.bashrc中加如下内容:export...
- C/C++|头文件、源文件分开写的源起及作用
-
1C/C++编译模式通常,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码;而.h文件则被称...
- linux中内部变量,环境变量,用户变量的区别
-
unixshell的变量分类在Shell中有三种变量:内部变量,环境变量,用户变量。内部变量:系统提供,不用定义,不能修改环境变量:系统提供,不用定义,可以修改,可以利用export将用户变量转为环...
- 在Linux中输入一行命令后究竟发生了什么?
-
Linux,这个开源的操作系统巨人,以其强大的命令行界面而闻名。无论你是初学者还是经验丰富的系统管理员,理解在Linux终端输入一条命令并按下回车后发生的事情,都是掌握Linux核心的关键。从表面上看...
- Nodejs安装、配置与快速入门(node. js安装)
-
Nodejs是现代JavaScript语言产生革命性变化的一个主要框架,它使得JavaScript从一门浏览器语言成为可以在服务器端运行、开发各种各样应用的通用语言。在不同的平台下,Nodejs的安装...
- Ollama使用指南【超全版】(olaplex使用方法图解)
-
一、Ollama快速入门Ollama是一个用于在本地运行大型语言模型的工具,下面将介绍如何在不同操作系统上安装和使用Ollama。官网:https://ollama.comGithub:http...
- linux移植(linux移植lvgl)
-
1uboot移植l移植linux之前需要先移植一个bootlader代码,主要用于启动linux内核,lLinux系统包括u-boot、内核、根文件系统(rootfs)l引导程序的主要作用将...
- Linux日常小技巧参数优化(linux参数调优)
-
Linux系统参数优化可以让系统更加稳定、高效、安全,提高系统的性能和使用体验。下面列出一些常见的Linux系统参数优化示例,包括修改默认配置、网络等多方面。1.修改默认配置1.1修改默认编辑器默...
- Linux系统编程—条件变量(linux 条件变量开销)
-
条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等...
- 面试题-Linux系统优化进阶学习(linux系统的优化)
-
一.基础必备优化:1.关闭SElinux2.FirewalldCenetOS7Iptables(C6)安全组(阿里云)3.网络管理服务||NetworkManager|network...
- 嵌入式Linux开发教程:Linux Shell
-
本章重点介绍Linux的常用操作和命令。在介绍命令之前,先对Linux的Shell进行了简单介绍,然后按照大多数用户的使用习惯,对各种操作和相关命令进行了分类介绍。对相关命令的介绍都力求通俗易懂,都给...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)