当前位置: 首页 > news >正文

Netty的几种IO模式的实现与切换

写在文章开头

今天我们就基于Netty来简单聊聊开发中几种常见的IO模式以及Netty对于这几种IO模式的实现,希望对你有帮助。

在这里插入图片描述

Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一,熟悉 Java 也会一点 Go ,偶尔也会在 C源码 边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号: 写代码的SharkChili

因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

在这里插入图片描述

详解几种常见的IO模式

同步与异步

在了解IO模式之前我们需要了解几个重要的概念,先来聊聊同步与非同步的概念,同步即意味着若内核缓冲区存在就绪数据时,应用程序中到内核中主动read就绪数据:

在这里插入图片描述

而异步则是应用程序到系统内核获取就绪数据时,若没有数据则直接返回并注册回调,当有就绪的数据时,直接通过回调通知进程:

在这里插入图片描述

阻塞与非阻塞

网络IO中的阻塞和非阻塞,这个概念是针对应用程序数据读取阶段的,如果是阻塞读,当应用程序通过read方法到系统内核缓冲区中获取,如果缓冲区没有数据,那么当前线程就会阻塞。对应的写请求也是一样,如果缓冲区已满那么线程也会阻塞等待有足够空间再进行写入:

在这里插入图片描述

而非阻塞读取则反之,当内核缓冲区没有就绪数据时,系统调用会直接返回,写请求同理:

在这里插入图片描述

Netty提供的几种IO模式

有了上述概念的基础,我们就可以正式的介绍如下几种IO模式,它们分别是:

  1. BIO:同步阻塞模型,由上述的概念我们可以知晓这种模式如果在没有就绪数据时,线程会阻塞等待,一旦数据就绪之后也是主动调用系统内核函数主动阻塞read获取数据。
  2. NIO:同步非阻塞,很明显这种模式下到系统内核发现没有就绪的数据会直接返回,一旦有了就绪数据也是主动调用read函数获取。
  3. AIO:异步非阻塞,这种模式理论上是性能表现最出色的,系统内核没有就绪的事件它会直接注册回调并返回,后续一旦有就绪的数据则通过回调主动将数据推送给应用程序。

Netty中已经对这种模型做了封装,我们以BIO创建服务端为例对应的代码示例如下,可以看到我们的线程组和channel都是采用OiOold io模式。

这里补充说明一下,很多人认为BIO模式性能表现一定会差于NIO,这一点笔者是不认同的,个人认为对于连接少、并发度较低的场景, 的线程专注于处理这些仅有的连接的设计性能表现会更加出色:

EventLoopGroup bossGroup = new OioEventLoopGroup(1);//设置为BIO的事件轮询器EventLoopGroup workerGroup = new OioEventLoopGroup();final EchoServerHandler serverHandler = new EchoServerHandler();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup)//设置服务端channel为BIO.channel(OioServerSocketChannel.class);//......

如果我们希望切换为NIO则直接替换group创建和channel即可:

		//设置线程组为NIOEventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();final EchoServerHandler serverHandler = new EchoServerHandler();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup)//设置channel为NIO类型.channel(NioServerSocketChannel.class);//......

需要了解的是Netty并没有为AIO进行相应的实现,其原因如下:

  1. AIOLinux上相较于epoll这种NIO模型性能没有太大的提升。
  2. AIOWindows上有着较成熟的方案,但是市场主流都是采用Linux作为主流服务器。
  3. AIOLinux上的实现还是不够成熟。

Netty对于IO模式自由切换的设计

可以看到Netty对于IO模式的切换只需在channel上进行简单的配置即,这一点它是如何设计与实现的呢?本质上channel方法采用的泛型抽象+工厂模式并结合反射这种理念,在配置引导类时,通过channel方法传入不同的IO模式策略class,其内部会基于这个class将其封装为channel工厂,后续服务初始化时就会基于这个channel工厂通过反射的方式生成服务端channel

在这里插入图片描述

这一点我们步入AbstractBootstrap的channel方法就可以看到,它会将我们的泛型class封装为反射工厂ReflectiveChannelFactory并通过channelFactory赋值给AbstractBootstrapchannelFactory

public B channel(Class<? extends C> channelClass) {//基于channelClass将其封装为ReflectiveChannelFactory然后复制给channelFactoryreturn channelFactory(new ReflectiveChannelFactory<C>(ObjectUtil.checkNotNull(channelClass, "channelClass")));}

后续服务端初始化方法initAndRegister就会通过channelFactorynewChannel反射生成服务端channel再调用init完成初始化:

final ChannelFuture initAndRegister() {Channel channel = null;try {//调用newChannel进行反射创建channelchannel = channelFactory.newChannel();//初始化channelinit(channel);} catch (Throwable t) {//......}//......}

于是其内部就来到我们的实现ReflectiveChannelFactorynewChannel,可以看到它通过反射完成channel创建,很明显这种通过泛型抽象,工厂懒加载的设计很好的维护的channel的创建时机和拓展:

 @Overridepublic T newChannel() {try {return constructor.newInstance();} catch (Throwable t) {throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);}}

默认NIO已经实现了epoll,为什么Netty还需要自实现呢?

NIO默认已经实现了epoll,但是Netty还是自实现了一版本的epoll

  b.group(bossGroup, workerGroup).channel(EpollServerSocketChannel.class)

这样做的其实是一种自信,NIO默认情况下是水平触发某些场景下开销很大,而Netty是支持自行配置水平触发和边缘触发,EpollServerSocketChannel在此基础上做了很多的参数封装,提供开发有更多的灵活的选择,对于后续的优化是可控的,且Netty实现的NIO相较于JDK默认的实现产生的垃圾更少且性能表现更出色。

小结

本文通过源码示例结合源码简单介绍了几种IO模式和Netty实现和设计理念,希望对你有帮助。

我是 sharkchiliCSDN Java 领域博客专家开源项目—JavaGuide contributor,我想写一些有意思的东西,希望对你有帮助,如果你想实时收到我写的硬核的文章也欢迎你关注我的公众号: 写代码的SharkChili
因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Flask基础教程(第一阶段)
  • JAVA—面向对象编程高级
  • 《死侍与金刚狼》票房飘红! 目前全球票房总票房$7亿,预计可达$12亿,全球排名跃居第二!
  • 数据集相关类代码回顾理解 | sns.distplot\%matplotlib inline\sns.scatterplot
  • 【redis 第八篇章】链表结构
  • 新增道路查询后的最短距离
  • YOLOv8添加注意力模块并测试和训练
  • 【VS Code】launch.json与tasks.json
  • Java 并发编程:volatile 关键字介绍与使用
  • 【系统架构设计师】二十四、安全架构设计理论与实践④
  • 安装ubuntu server24.04系统
  • 浅谈 Spring AOP框架 (1)
  • 数据湖之Hudi
  • Java 技巧:将整数每一位数字转换为数组
  • 【C++题解】1015. 鸡兔同笼问题
  • php的引用
  • __proto__ 和 prototype的关系
  • 【翻译】babel对TC39装饰器草案的实现
  • angular组件开发
  • CSS魔法堂:Absolute Positioning就这个样
  • HTML5新特性总结
  • IDEA常用插件整理
  • Java应用性能调优
  • mysql中InnoDB引擎中页的概念
  • MySQL主从复制读写分离及奇怪的问题
  • Odoo domain写法及运用
  • php中curl和soap方式请求服务超时问题
  • Puppeteer:浏览器控制器
  • Service Worker
  • vue的全局变量和全局拦截请求器
  • Vue组件定义
  • 从零开始的无人驾驶 1
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 基于webpack 的 vue 多页架构
  • 你不可错过的前端面试题(一)
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • $refs 、$nextTic、动态组件、name的使用
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (35)远程识别(又称无人机识别)(二)
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (PADS学习)第二章:原理图绘制 第一部分
  • (pojstep1.3.1)1017(构造法模拟)
  • (Qt) 默认QtWidget应用包含什么?
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (一)SvelteKit教程:hello world
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)h264中avc和flv数据的解析