【Netty 从成神到升仙系列 大结局】全网一图流死磕解析 Netty 源码

news2024/11/20 13:28:22
  • 👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,Java领域新星创作者。
  • 📝个人公众号:爱敲代码的小黄
  • 📕系列专栏:Java设计模式、数据结构和算法
  • 📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
  • 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
  • 🍂博主正在努力完成2022计划中:以梦为马,扬帆起航,2022追梦人

@[Toc]

全网一图流死磕解析 Netty 源码

通过之前介绍的几篇关于 Netty 的文章,相信大家多少对 Netty 有了一点了解,本篇文章主要从整个 Netty 的调用流程图来做一个汇总

往期文章:

  • 【Netty 从成神到升仙系列 五】Netty 的责任链真有这么神奇吗?
  • 【Netty 从成神到升仙系列 四】让我们一起探索 Netty 中的零拷贝
  • 【Netty 从成神到升仙系列 三】Netty 凭什么成为国内最流行的网络通信框架?
  • 【Netty 从成神到升仙系列 二】你真的懂 NIOEventLoop 嘛?
  • 【Netty 从成神到升仙系列 一】Netty 服务端的启动源码剖析(一)

一、Netty 服务端的启动

Netty 服务端的整个启动流程,我做成了一个流程图:

在这里插入图片描述

高清的可在公众号 爱敲代码的小黄 回复:Netty,即可获取

这个我们之前讲过,在 【Netty 从成神到升仙系列 一】Netty 服务端的启动源码剖析(一),当时通过源码的角度剖析了下 Netty 的启动流程。

由于当时对于 Netty 整体的把控不太好,有一些细节性的东西选择性的忽略,导致最终整体的连贯性差强人意

后来,学完 Netty 之后,又对服务端的启动做了一些细致化的分析

1. Java NIO 的启动

我们学过 Netty 的都知道,Netty 无非在 Java NIO 的基础上做了一定的优化,使得 I/O 网络的效率大大提高

但从整体来说,Netty 服务端的启动和 Java NIO 的启动基本一模一样,主要集中在以下几方面:

  • 创建一个 selector 对象

    Selector selector = Selector.open();
    ServerSocketChannel ssc = ServerSocketChannel.open(); // 创建FD-1
    ssc.configureBlocking(false); // 非阻塞模式
  • 建立 selector 与 channel 的联系(注册)

    SelectionKey sscKey = ssc.register(selector, SelectionKey.OP_ACCEPT, null);
  • 注册端口号

    ssc.bind(new InetSocketAddress(8080));

而我们 Netty 用了好多好多的代码去描述了上述几行代码。

当我看完第一遍 Netty 服务端启动流程,感觉这样写的好处在哪呢,完全没有 GET 到,当我第二遍带着疑惑去看并画清流程图之后,发现 Netty 的源码确实令人耳目一新

具体表现在哪些方面呢,我们可以继续往下看

2. Netty 服务端的启动

我们看到,最核心的当属 Pipline,这里我们也不多介绍了,之前也都讲过。

从整体来看,其实和我们写的业务代码也差不多,但博主感觉比较厉害的一点就是:对于一些我们不需要立即获取结果的处理,全部封装成 Task 交给线程处理,这样会让我们的系统线程的使用率提高并且性能提升。

在轮询处理时,也会使用 ioRatio 控制 I/O 的比例,同时依靠计数器解决了 Epoll 空轮询的BUG。

我们初始化时,分别有 BOSSWORK 两个线程组,我们的 BOSS 线程组主要用来接受客户端的连接事件,而 WORK 线程组用来处理客户端的读事件。

二、Netty 服务端的读写

通过上述 Netty 服务端的启动,我们向我们的 Selector 上注册了 OP_ACCEPT 事件,当有客户端连接到服务端时,就会触发该事件。

1.注册读事件

通过上述的流程图我们可以看到,通过 Channel 的不同来实现不同的 unsafe.read() 的实现

这里 channel 不同主要指的是:NioServerSocketChannelNioSocketChannel 两种

对于 NioServerSocketChannel 来说,负责接受当前客户端的 连接请求,生成 NioSocketChannel 将当前的连接注册到 Work 的线程组上(childGroup.register(child))并向 Selector 上注册 读事件(接受客户端)

2.读数据

而对于 NioSocketChannel,主要负责处理客户端的 读请求

对于读数据来说,主要通过 allocHandle.lastBytesRead(doReadBytes(byteBuf)) 进行读取

这里说下读取及扩容的逻辑:

  • 服务端默认接受客户端的 byteBuf 是 1024
  • NettybyteBuf 可以根据接受数据的大小进行动态的扩缩容(SIZE_TABLE),规律如下:
    • 扩容:当小于 512 时,一次性增加 64,当大于 512 时,一次性增加 16倍
    • 缩容:当小于 512 时,一次性减少 16,当大于 512 时,一次性减少 一倍
    • 当然,源码中扩缩容的前提不同:缩容需要连续两次都小于,而扩容只需要大于一次就可以
    • byteBuf 扩容也是有范围限制的:默认在64和65536之间
  • 什么时候停止数据的读取,主要由 allocHandle.continueReading() 控制
    • 当前的数据已被读取完毕
    • 当前的数据被读取16次,需要结束
    • 这里规定读取次数的原因:控制次数,防止无限次循环,浪费线程。

3.写数据

上面在读取数据时,会调用 pipeline.fireChannelRead(byteBuf),这里会执行 Pipline 上的各个 HandlerchannelRead方法,这里我们一般使用 writeAndFlushwrite,我们分开来讲。

首先,对于 write 的方法,主要由 incrementPendingOutboundBytes(long size, boolean invokeLater) 执行,这里会向 ChannelOutboundBuffer 写入信息并判断当前写入的数据是否超越水位线(默认 64 * 1024)。

如果超越了我们的水位线,我们会给与 ChannelOutboundBuffer 一个标记,将 unwritable = 0(false) 修改为 unwritable = 1(true),这样在我们后续发送的过程中,可以根据此标记让应用自己选择是否发送。

这里的 ChannelOutboundBuffer 主要相当于一个容器,write 向里面写,Flush 往内核发。

4.刷数据

这里的刷数据,指的是用户空间的 Buffer 向内核空间的 Scoket 刷数据,类似:

在这里插入图片描述

我们的数据会发送到 Socket 缓冲区,由网卡发出。

我们来讲一讲 NettyFlush 流程:

  • 判断当前通道是否关闭(防止当前的客户端已经断开连接)
  • 刷数据
    • case 0:文件数据,通过零拷贝刷,这里之前讲过,参考:【Netty 从成神到升仙系列 四】让我们一起探索 Netty 中的零拷贝
    • case 1:单个数据的写入
    • default:批量数据的写入:由于批量数据写入,必然存在缓冲区满的问题,当缓冲区满的时候,Netty 会注册一个 写事件,当缓冲区有空闲时,触发写事件,交由其他线程处理。
  • 如果写了16次数据还没有写完的话,会新起一个 flush 任务,让其余的 Work 线程执行该任务。这里不需要写事件的原因:当前的缓存区是空闲的,如果注册了写事件,会造成不断的有写事件触发。

这里可能有小伙伴们疑惑,为什么一个写事件就能避免批量数据写入的问题,缓冲区满与空闲代表什么?

当我们的服务端收到客户端的请求时,我们会 writeflush,我们从整个流程看下: 在这里插入图片描述

对于服务端来说,我们的 flush 操作会将处理用户态的 buffer 中的数据刷到内核态的 Scoket缓冲区

对于客户端来说,会通过网卡接受服务端的数据并刷新到 Scoket缓冲区 中,通过用户态的 buffer 读取

如果我们当前的服务端发送的数据过大,客户端接受的数据过少,就会导致服务端这边的 Scoket缓冲区 阻塞

  • 87380 :tcp 接收缓冲区的默认值
  • 16384 : tcp 发送缓冲区的默认值

这个时候,我们只能等待 Scoket缓冲区 有空闲时,继续向里面 flush,但这种无疑会把当前的线程阻塞住,违背 NIO 的架构初衷

所以,当批量的数据一次性写入不成功时,我们会向 Selector 注册一个写事件,当 Scoket缓冲区 有空闲时,会触发该事件,并交由其他的 work 线程去处理,这样极大的发挥了 Netty 高效的网络通讯框架的作用。

三、总结

自此,Netty 的章节基本就结束了~

不得不说,学习 Netty 的过程还是挺痛苦的,有一些网络层面的东西,之前都没有接触过,只能慢慢的去补

还有一些学习中的疑惑,也需要大量的时间去消化,比如:

  • Netty 为什么能成为最高效的网络 I/O 框架?
    • 当你刚学习时,你可能会听说 NIO 使 Netty 变的有效起来
  • NIO 是什么?为什么会出现 NIO?
    • 这个时候你会了解到,主要由于 C10K 的经典问题出现,导致了 NIO 的出现
    • NIO 实际上是 Linux 下 IO多路复用的机制,通过 select、poll、epoll三种方式来实现
  • select、poll、epoll 是什么?
    • 了解到每个实现方式的不同以及慢慢的优化,最终采用的 epoll 作为当前 NIO 的实现
  • 网络是怎么发送的?Socket 的本质又是什么?
  • Linux 网络编程又是如何实现的?

其实,慢慢的思索,好像学习一个知识,会带来一系列的蝴蝶效应,尤其是对下层知识的缺乏,导致自己上层知识的不稳定。

就像我一开始其实是分析的 kafka 的源码,到了通信那一节,看不懂了

于是去看了看 IO,继而又去看 操作系统计算机网络

不过用了这么多时间,还算有成效,至少 Netty 可以懂了一点了

本期的内容就到这里,后续的话,可能会出一篇 select、poll、epoll 的文章,其次,后面应该会重新回到 kafka 的源码解析来

我是一名独角兽企业的Java开发工程师,希望可以点个关注呀,有问题可以留言或者私信,我们下期再见!

本文由博客一文多发平台 OpenWrite 发布!

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

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

相关文章

第八篇 python 面向对象编程

11 面向对象编程 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。 面向过程的程序设计把计算机程序视为一系列的命令集合,即一组…

Python攻防-APK批量化Pull与自动化反编译

文章目录前言Pull APK根据包名列表根据手机路径逆向APK自动化反编译findstr检索…总结前言 日常工作过程中,经常会遇到发现新的攻击模式的情况下,需要全量排查手机上所有 APP 的代码是否存在该类代码缺陷。对于复杂的攻击模式而言,往往需要动…

【MyBatis框架】动态SQL

MyBatis之动态SQL 目录MyBatis之动态SQL1. < if > 元素2. < where >3. < choose >,< when >,< otherwise >元素4. < trim >元素5. < set >元素6. < foreach >元素6.1 添加批量数据6.2 批量删除数据7. < SQL >元素8. 小结…

LVS负载均衡群集

企业群集应用 1. 群集的含义 1.1Cluster&#xff0c;群集&#xff0c;集群 2.1由多台主机构成&#xff0c;但对外&#xff0c;只表现为一个整体&#xff0c;只提供一个访问入口&#xff08;域名或ip地址&#xff09;&#xff0c; 相当于一台大型计算机 2.问题出现 互联网…

Sentinel的学习

1、Sentinel控制台的下载 下载地址&#xff1a;https://github.com/alibaba/Sentinel/releases/tag/1.8.3 2、Sentinel控制台的启动 java -jar sentinel-dashboard-1.8.3.jar3、访问 浏览器输入&#xff1a;localhost:8080 账号密码&#xff1a; sentinel/sentinel 4.sprin…

SARScape中用sentinel-1数据做SBAS-InSAR完整流程(1/2)

SARScape中用sentinel-1数据做SBAS-InSAR完整流程1 SABA-InSAR原理简述2 数据采集和预设2.1 SAR数据采集2.2 DEM数据下载与放置2.3 精密轨道数据下载与放置2.4 制作研究区范围矢量2.5 SARscape Preferences预设3 SAR数据预处理3.1 导入数据3.2 optional files设置3.3 参数设置4…

【Git】Git使用的三个场景总结 | 远程仓库到本地 | 本地获取git仓库 | 远程仓库与本地相连接

&#x1f4ad;&#x1f4ad; ✨&#xff1a; git使用的三个场景总结 | 远程仓库到本地 | 本地获取git仓库 | 远程仓库与本地相连接   &#x1f49f;&#xff1a;东非不开森的主页   &#x1f49c;&#xff1a;学习的过程就是不断接触错误&#xff0c;不断提升自己&#xff0c…

Linux 卸载zabbix图文教程

Linux 卸载zabbix图文教程前言1.停止zabbix服务2.卸载zabbix服务2.1查找zabbix所有被安装的rpm包2.2卸载zabbix服务2.3删除所有与zabbix相关的文件&#xff08;配置项等&#xff09;3.卸载数据库3.1查找mariadb所有被安装的rpm包&#xff0c;并删除3.2删除mysql相关配置文件4.卸…

Source Insight4.0中文注释乱码解决方案

一、Source Insight软件介绍 Source Insight是一个面向项目的编程编辑器、代码浏览器和分析器&#xff0c;可帮助您在工作和计划​​时分析代码&#xff0c;具有针对 C/C、C#、Java、Objective-C 等的内置动态分析&#xff0c;深受众多嵌入式软件开发者的喜爱。 二、中文乱码…

复旦-华盛顿大学EMBA 二十年20人丨徐欣:从外企转战民企的变身

复旦大学-华盛顿大学EMBA20周年校友系列访谈。      2008年堪称转折之年&#xff0c;中国举行北京奥运会向全世界展示“和而不同”的理念&#xff0c;入世7年让中国在贸易、金融领域与全球市场紧密相连&#xff0c;一大批最优秀的中国民营企业也加速踏上全球化之路。    …

Web APIs:PC 端网页特效--动画函数封装

动画原理 核心原理&#xff1a;通过定时器 setInterval() 不断移动盒子位置 实现步骤&#xff1a; 1. 获得盒子当前位置 2. 让盒子在当前位置加上1个移动距离 3. 利用定时器不断重复这个操作 4. 加一个结束定时器的条件 5. 注意此元素需要添加定位&#xff0c;才能使用e…

【C语言】三子棋小游戏

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f40c; 个人主页&#xff1a;蜗牛牛啊 &#x1f525; 系列专栏&#xff1a;初出茅庐C语言 ☀️ 学习格言&#xff1a;眼泪终究流不成海洋&#xff0c;人总要不断成长&am…

Selenium基础 — iframe表单操作

1、什么是iframe表单 实际上就是HTML页面中使用iframe/frame标签&#xff0c;是在当前页面中引用了其他页面的链接&#xff0c;真正的页面数据并没有出现在当前页面源码中&#xff0c;但是在浏览器中我们时看到的。简单理解可以使页面中开了一个窗口显示另一个页面。 我们在We…

谷粒商城-支付业务

目录 商城业务-支付-支付宝沙箱&代码 商城业务-支付-RSA、加密加签、密钥等 商城业务-支付-内网穿透 商城业务-订单服务-整合支付前需要注意的问题 商城业务-订单服务-整合支付 商城业务-订单服务-支付成功同步回调 商城业务-订单服务-订单列表页渲染完成 商城业务…

网络请求+基于Node.js的WebSocket

目录 前言 网络访问配置 1.配置流程 注意事项 使用限制 网络请求详情API wx.request请求数据API ​编辑 wx.uploadFile文件上传API wx.downloadFile文件下载API WebSocket会话API 基于Node.js的WebSocket 为什么WebSocket连接可以实现全双工通信而HTTP连接不行呢&…

git命令记不住?可视化git操作平台Sourcetree入门教程

1、为什么要用Sourcetree 在应届生在参加实习或者工作的时候&#xff0c;往往需要配置各种各样的环境&#xff0c;git肯定是程序员必不可少的分布式版本控制系统&#xff0c;但刚出来工作时往往对git代码不熟悉&#xff0c;老是会忘掉一些命令&#xff0c;所以笔者在此推荐一个…

算法《第四版》笔记整理

算法第四版 先导例子&#xff1a;动态连通性 - 书中1.5 知识点&#xff1a;并查集-一种用于解决动态连通性问题的算法 描述&#xff1a;对于N个对象&#xff0c;有两种操作&#xff1a;1.连接两个对象 2.判断两个对象是否存在连接路径 如巨大的连通性问题&#xff1a; 在分析…

【力扣刷题】Day32——单调栈专题

文章目录单调栈1.每日温度2.下一个更大元素 I3.下一个更大元素II4. 接雨水5.柱状图中最大的矩形单调栈 单调栈基础知识回顾&#xff1a;单调栈与单调队列_塔塔开!!!的博客-CSDN博客_单调栈 单调队列 单调栈一般模板&#xff1a; int[] stk new int[N] //Stack<Integer>…

倒排索引-字符串相似匹配(结巴分词、中文转拼音)

工作中&#xff0c;遇到有两个不同的系统&#xff0c;两个系统中有相同的功能&#xff0c;维护一个主播的名称。现在准备将两个系统的主播合并到一起。因为主播名称可能由不同的人维护的&#xff0c;他们也不知道主播的真实姓名&#xff0c;比如一条小团团&#xff0c;可能维护…

香橙派3LTS部署ROS2阿克曼开源平台

1.系统镜像下载 这里我们需要安装ROS2的humble版本&#xff0c;需要ubuntu 22.04版本的系统。 香橙派镜像下载&#xff1a;http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-3-LTS.html 点击Ubuntu镜像&#xff0c;选择jamm…