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

Netty Review - 服务端channel注册流程源码解析

文章目录

  • Pre
  • Netty主从Reactor线程模型
  • 服务端channel注册流程
  • 源码解读
    • 入口 `serverBootstrap.bind(port)`
  • 源码流程图

在这里插入图片描述

在这里插入图片描述


Pre

Netty Review - ServerBootstrap源码解析

Netty Review - NioServerSocketChannel源码分析


Netty主从Reactor线程模型

Netty 使用主从 Reactor 线程模型来处理并发连接和网络事件。

在 Netty 中,通常有两种类型的线程池:

  1. Boss 线程池:用于接受客户端连接请求,并将接受到的连接注册到 Worker 线程池的 EventLoop 中。Boss 线程池中的线程负责监听 ServerSocketChannel,并将接受到的连接分配给 Worker 线程池中的某个 EventLoop 处理。

  2. Worker 线程池:每个 Worker 线程池包含多个 EventLoop,每个 EventLoop 负责处理一组连接的读写和事件处理。当一个连接被注册到某个 Worker 线程池的 EventLoop 中时,该 EventLoop 将负责处理这个连接的所有事件,包括读取数据、写入数据、处理网络事件等。


主从 Reactor 线程模型的工作流程如下:

  1. 主线程池(Boss 线程池)负责监听 ServerSocketChannel 上的连接请求,并将接受到的连接请求分配给 Worker 线程池中的某个 EventLoop。

  2. Worker 线程池中的每个 EventLoop 都独立负责一组连接的读写和事件处理。当一个连接被注册到某个 EventLoop 上时,该 EventLoop 将会不断地轮询连接上是否有可读事件或可写事件,并在事件发生时进行相应的处理。

  3. 当有读写事件发生时,EventLoop 将调用对应的 ChannelHandler 进行处理。这些 ChannelHandler 可以进行数据解析、业务逻辑处理等操作。

  4. 处理完事件后,EventLoop 可能会将结果写回到连接中,或者关闭连接等。

通过主从 Reactor 线程模型,Netty 可以高效地处理大量的并发连接和网络事件,提高了网络应用程序的性能和可扩展性。


服务端channel注册流程

在Netty中,服务端Channel注册流程涉及以下几个关键步骤:

  1. 创建ServerBootstrap实例: 首先,需要创建一个ServerBootstrap实例,它是Netty提供的用于启动服务端的引导类。

  2. 配置ServerBootstrap: 使用ServerBootstrap实例,设置一系列参数,包括线程模型、Channel类型、处理器等。

  3. 绑定端口并启动服务: 调用ServerBootstrap的bind方法,指定端口并启动服务端。在bind方法内部,会进行以下操作:

    • 创建NioServerSocketChannel实例:用于表示服务端的Channel,内部封装了Java NIO中的ServerSocketChannel。

    • 初始化ChannelPipeline:为NioServerSocketChannel实例创建一个ChannelPipeline对象,用于管理ChannelHandler链。

    • 创建ChannelInitializer并添加到ChannelPipeline:ChannelInitializer是一个特殊的ChannelHandler,它用于在Channel注册到EventLoop之后初始化ChannelPipeline。在ChannelInitializer的initChannel方法中,可以向ChannelPipeline中添加自定义的ChannelHandler。

    • 获取EventLoopGroup并注册Channel:从ServerBootstrap中获取Boss EventLoopGroup,然后调用其register方法注册NioServerSocketChannel到EventLoop上。

  4. 注册Channel到EventLoop: 在调用register方法时,会将NioServerSocketChannel注册到Boss EventLoop上。在注册过程中,会执行以下操作:

    • 获取EventLoop:根据配置,从Boss EventLoopGroup中选择一个EventLoop。

    • 调用EventLoop的register方法:将NioServerSocketChannel注册到选定的EventLoop上。注册过程中,会创建一个NioServerSocketChannelUnsafe实例来处理注册过程,其中会调用底层的Java NIO方法将ServerSocketChannel注册到Selector上,并监听ACCEPT事件。

  5. 事件处理: 一旦NioServerSocketChannel注册到了EventLoop上,就会开始监听ACCEPT事件。当有新的连接接入时,会触发ACCEPT事件,EventLoop会调用相关的ChannelHandler进行处理,如调用ChannelInitializer的initChannel方法,添加用户自定义的ChannelHandler到新的连接的ChannelPipeline中。接着,新的连接就可以接受和处理客户端的请求了。

通过以上流程,服务端Channel在Netty中的注册过程就完成了,它可以接受客户端的连接,并将连接注册到EventLoop上进行事件处理。


源码解读

当我们梳理完

Netty Review - ServerBootstrap源码解析

Netty Review - NioServerSocketChannel源码分析

接下来让我们从下面这一行代码开始

ChannelFuture channelFuture = serverBootstrap.bind(9000).sync();
channelFuture.channel().closeFuture().sync();

这段代码用于启动服务端并阻塞当前线程直到服务端关闭。

  1. serverBootstrap.bind(9000):调用serverBootstrapbind()方法绑定端口9000,并返回一个ChannelFuture对象,表示绑定操作的异步结果。

  2. .sync():调用sync()方法阻塞当前线程,直到绑定操作完成。这样做是为了确保服务端在端口绑定完成后再继续执行后续代码。

  3. channelFuture.channel().closeFuture().sync():获取channelFuture中的channel(),然后调用其closeFuture()方法获取一个表示关闭操作的ChannelFuture对象。接着,再次调用sync()方法阻塞当前线程,直到关闭操作完成。这样做是为了让当前线程一直等待直到服务端关闭。

入口 serverBootstrap.bind(port)

这段代码是bind(int inetPort)方法的实现。让我们逐步解释其含义:

/*** Create a new {@link Channel} and bind it.*/
public ChannelFuture bind(int inetPort) {// 调用bind方法,传入一个InetSocketAddress对象,该对象使用指定的端口号创建return bind(new InetSocketAddress(inetPort));
}

创建一个新的Channel并绑定到指定的端口。


doBind(final SocketAddress localAddress)

这段代码是doBind(final SocketAddress localAddress)方法的实现。

private ChannelFuture doBind(final SocketAddress localAddress) {// 初始化并注册Channel,并返回一个ChannelFuturefinal ChannelFuture regFuture = initAndRegister();// 获取注册完成的Channelfinal Channel channel = regFuture.channel();// 如果注册过程中发生异常,则直接返回注册的ChannelFutureif (regFuture.cause() != null) {return regFuture;}if (regFuture.isDone()) {// 如果注册已经完成,则创建一个新的ChannelPromise,并执行绑定操作ChannelPromise promise = channel.newPromise();doBind0(regFuture, channel, localAddress, promise);return promise;} else {// 如果注册尚未完成,则创建一个PendingRegistrationPromise,并添加一个监听器等待注册完成后再执行绑定操作final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {Throwable cause = future.cause();if (cause != null) {// 如果注册过程中发生异常,则直接设置失败状态promise.setFailure(cause);} else {// 注册成功后执行绑定操作promise.registered();doBind0(regFuture, channel, localAddress, promise);}}});return promise;}
}

这段代码的作用是执行绑定操作,并返回一个与绑定操作相关的ChannelFuture。


initAndRegister()

final ChannelFuture initAndRegister() {Channel channel = null;try {// 使用channelFactory创建一个新的Channel实例channel = channelFactory.newChannel();// 对新创建的Channel进行初始化init(channel);} catch (Throwable t) {if (channel != null) {// 如果初始化过程中发生异常,关闭Channelchannel.unsafe().closeForcibly();// 创建一个新的ChannelPromise,并设置失败状态return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);}// 如果channel为null,则创建一个FailedChannel实例,并设置失败状态return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);}// 使用ChannelConfig的EventLoopGroup进行注册ChannelFuture regFuture = config().group().register(channel);// 如果注册过程中发生异常,则关闭Channelif (regFuture.cause() != null) {if (channel.isRegistered()) {channel.close();} else {channel.unsafe().closeForcibly();}}// 返回注册的ChannelFuturereturn regFuture;
}

创建一个新的Channel实例并对其进行初始化,然后使用EventLoopGroup将其注册到事件循环中。最后返回一个与注册操作相关的ChannelFuture。


channelFactory.newChannel()

channelFactory.newChannel() 中的实现,请移步 Netty Review - NioServerSocketChannel源码分析

init(channel)

@Override
void init(Channel channel) throws Exception {// 设置Channel的选项final Map<ChannelOption<?>, Object> options = options0();synchronized (options) {setChannelOptions(channel, options, logger);}// 设置Channel的属性final Map<AttributeKey<?>, Object> attrs = attrs0();synchronized (attrs) {for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {@SuppressWarnings("unchecked")AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();channel.attr(key).set(e.getValue());}}// 获取Channel的PipelineChannelPipeline p = channel.pipeline();// 复制当前的子组、子处理器、子选项和子属性final EventLoopGroup currentChildGroup = childGroup;final ChannelHandler currentChildHandler = childHandler;final Entry<ChannelOption<?>, Object>[] currentChildOptions;final Entry<AttributeKey<?>, Object>[] currentChildAttrs;synchronized (childOptions) {currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));}synchronized (childAttrs) {currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));}// 向Channel的Pipeline中添加一个ChannelInitializerp.addLast(new ChannelInitializer<Channel>() {@Overridepublic void initChannel(final Channel ch) throws Exception {final ChannelPipeline pipeline = ch.pipeline();ChannelHandler handler = config.handler();if (handler != null) {// 添加用户配置的处理器到Pipeline中pipeline.addLast(handler);}// 在Channel的事件循环中执行ch.eventLoop().execute(new Runnable() {@Overridepublic void run() {// 添加一个ServerBootstrapAcceptor到Pipeline中,用于接收新连接pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));}});}});
}

init()方法的作用是初始化Channel,设置Channel的选项和属性,然后向ChannelPipeline中添加一个ChannelInitializer,该InitializerChannel的事件循环中执行,并向Pipeline中添加一个ServerBootstrapAcceptor,用于接收新连接。


config().group().register(channel)

在这里插入图片描述

    @Overridepublic ChannelFuture register(Channel channel) {return next().register(channel);}

在这里插入图片描述

next()

io.netty.util.concurrent.DefaultEventExecutorChooserFactory.PowerOfTwoEventExecutorChooser#next
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {// 使用原子整数来维护索引,以确保在多线程环境中安全地获取下一个 EventExecutor 实例。private final AtomicInteger idx = new AtomicInteger();// 存储所有可用的 EventExecutor 实例的数组。private final EventExecutor[] executors;// 构造方法,接收一个 EventExecutor 实例数组作为参数,并将其存储在 executors 成员变量中。PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {this.executors = executors;}// 选择下一个要使用的 EventExecutor 实例。// 通过对索引进行按位与操作(idx.getAndIncrement() & executors.length - 1),// 来确保索引始终在 executors 数组的有效范围内。// 由于 executors.length 必须是 2 的幂次方,因此使用按位与运算(&)可以有效地实现取模操作,// 从而将索引限制在数组长度范围内。@Overridepublic EventExecutor next() {return executors[idx.getAndIncrement() & executors.length - 1];}
}

在这里插入图片描述



源码流程图

在这里插入图片描述

图都给你画好了,戳这里


在这里插入图片描述

相关文章:

Netty Review - 服务端channel注册流程源码解析

文章目录 PreNetty主从Reactor线程模型服务端channel注册流程源码解读入口 serverBootstrap.bind(port) 源码流程图 Pre Netty Review - ServerBootstrap源码解析 Netty Review - NioServerSocketChannel源码分析 Netty主从Reactor线程模型 Netty 使用主从 Reactor 线程模型…...

冒泡排序平均需要跑多少趟:拉马努金Q函数初探

摘要: 拉马努金Q函数在算法分析中的应用&#xff0c;初步体验 【对算法&#xff0c;数学&#xff0c;计算机感兴趣的同学&#xff0c;欢迎关注我哈&#xff0c;阅读更多原创文章】 我的网站&#xff1a;潮汐朝夕的生活实验室 我的公众号&#xff1a;算法题刷刷 我的知乎&#x…...

Shell 学习笔记(三)-shell变量

Shell 语言是一种动态类型和弱类型语言, 因此,在Shell中无需显示地声明变量, 且变量的类型会根据不同的操作符而发生变化. 静态类型语言: 在程序编译期间就确定变量类型的语言, 如java, C等 动态类型语言: 在程序运行期间才确定变量类型的语言, 如PHP, Python等. 一 shell变量…...

新冠:2022和2024两次新冠感染的对比

第一次 2022年底第一次放开管控&#xff0c;95%以上的人都感染了一次奥密克戎 症状 第一天&#xff1a;流涕&#xff0c;咽痛。 第二天&#xff1a;高烧40度&#xff0c;全身疼痛&#xff0c;动不了。没有胃口&#xff0c;头晕想吐。 吃了白加黑退烧药&#xff0c;清开灵颗粒…...

笔记:《NCT全国青少年编程能力等级测试教程Python语言编程二级》

NCT全国青少年编程能力等级测试教程Python语言编程二级 ISBN:9787302565857 绪论 专题1 模块化编程 考查方向 考点清单 考点 模块化编程 (一)模块化编程思想:结构清晰、降低复杂度;提高代码复用率;易于扩展、维护,方便阅读、优化。 …...

顶级思维方式——认知篇五(思想的觉醒)

目录 1、 女性的地位觉醒 2、电视剧《天道》之高人思维&#xff1a;丁元英为什么讲“人间黑白颠倒”&#xff1f; 3、 创业公司, 更应该大胆的创新. 4、 做到一定职务的时候&#xff0c; 你一定想到在你这个地位上你要做什么 1、 女性的地位觉醒 过去引以为鉴的例子&…...

面试技术栈 —— 2024网易雷火暑期实习真题

面试技术栈 —— 2024网易雷火暑期实习真题 1. 最长递增子序列。2. 集中限流和单机限流你觉得哪个好&#xff1f;3. redis部署服务器配置&#xff0c;为什么不用哨兵&#xff1f;4. 讲讲分布式session的原理。5. 数据库&#xff1a;表数据量大了&#xff0c;如何分表&#xff1…...

【小赛1】蓝桥杯双周赛第5场(小白)思路回顾

我的成绩&#xff1a;小白(5/6) 完稿时间&#xff1a;2024-2-13 比赛地址&#xff1a;https://www.lanqiao.cn/oj-contest/newbie-5/ 相关资料&#xff1a; 1、出题人题解&#xff1a;“蓝桥杯双周赛第5次强者挑战赛/小白入门赛”出题人题解 - 知乎 (zhihu.com) 2、矩阵快速幂&…...

docker (二)-yum二进制部署

yum安装docker&#xff08;Linux&#xff09; 安装环境&#xff1a;CentOS 7.9 一 如果之前安装了旧版docker&#xff0c;请先删除 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotat…...

【深度学习】S2 数学基础 P2 线性代数(下)

目录 范数的意义范数的数学意义范数之于深度学习的意义 L1 范数与 L2 范数L1 范数L2 范数 小结 本节博文是线性代数第二部分&#xff0c;主要内容为 L 1 L1 L1 范数与 L 2 L2 L2 范数&#xff1b;有关线性代数基础知识&#xff0c;请访问&#xff1a;【深度学习】S2 数学基础…...

【软考高级信息系统项目管理师--考试内容大纲篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;软考高级–信息系统项目管理师 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 软考高级信息系统项目管理师--考试内容大纲篇 1.信息化发展2.信息技术发展3.信息系…...

C语言——枚举类型

&#x1f4dd;前言&#xff1a; 在之前的文章中我们已经讲解了自定义类型中的结构体类型和联合体类型&#xff0c;现在我们再充分学习一下C语言中的枚举类型&#xff1a; 1&#xff0c;什么是枚举类型 2&#xff0c;枚举类型的定义和变量的声明 3&#xff0c;对变量进行赋值 &a…...

linux---内存管理

一 虚拟内存 即使是现代操作系统中&#xff0c;内存依然是计算机中很宝贵的资源&#xff0c;看看你电脑几个T固态硬盘&#xff0c;再看看内存大小就知道了。 为了充分利用和管理系统内存资源&#xff0c;Linux采用虚拟内存管理技术&#xff0c;利用虚拟内存技术让每个进程都有…...

v-model原理

v-model原理 v-model原理表单类组件封装v-model简化代码 v-model原理 1.原理&#xff1a; v-model本质上是一个语法糖。例如应用在输入框上&#xff0c;就是value属性 和 input 事件的合写 <template><div id"app" ><input v-model"msg"…...

波奇学Linux:文件系统

磁盘认识 磁盘被访问的基本单元是扇区-512字节。 磁盘可以看成多个同心圆&#xff0c;每个同心圆叫做磁道&#xff0c;多个扇区组成同心圆。 我们可以把磁盘看做由无数个扇区构成的存储介质。 要把数据存到磁盘&#xff0c;先定位扇区&#xff0c;用哪一个磁头&#xff0c;…...

项目访问量激增该如何应对

✨✨ 欢迎大家来到喔的嘛呀的博客✨✨ &#x1f388;&#x1f388;希望这篇博客对大家能有帮助&#x1f388;&#x1f388; 目录 引言 一. 优化数据库 1.1 索引优化 1.2 查询优化 1.3 数据库设计优化 1.4 事务优化 1.5 硬件优化 1.6 数据库配置优化 二. 增加服务器资源…...

【Linux环境基础开发工具的使用(yum、vim、gcc、g++、gdb、make/Makefile)】

Linux环境基础开发工具的使用yum、vim、gcc、g、gdb、make/Makefile Linux软件包管理器- yumLinux下安装软件的方式认识yum查找软件包安装软件如何实现本地机器和云服务器之间的文件互传卸载软件 Linux编辑器 - vimvim的基本概念vim下各模式的切换vim命令模式各命令汇总vim底行…...

幻兽帕鲁官方更新了,服务器端怎么更新?

幻兽帕鲁官方客户端更新了&#xff0c;那么它的服务器端版本也是需要更新的&#xff0c;不然版本不一致的话&#xff0c;就不能进入游戏了。 具体的更新方法有两种&#xff0c;一是手动输入命令进行更新。第二种是在面板一键更新。 无论你是在阿里云或者腾讯云购买的一键部署…...

axios-retry 响应异常

最近项目中遇到 axios 异步请求异常中断, 错误码为 “ECONNABORTED” 奇怪的是排查前端代码并没有发现有主动调用 abort 取消请求的 由于为何网络请求失败的原因找不到, 但是重试请求就是成功的, 所以计划使用 axios-retry 在网络错误时重新请求 import axiosRetry from axios…...

Vue项目创建和nodejs使用

Vue项目创建和nodejs使用 一、环境准备1.1.安装 node.js【下载历史版本node-v14.21.3-x64】1.2.安装1.3.检查是否安装成功&#xff1a;1.4.在Node下新建两个文件夹 node_global和node_cache并设置权限1.5.配置npm在安装全局模块时的路径和缓存cache的路径1.6.配置系统变量&…...

手把手教你用FUTURE POLICE:会议录音秒变带时间轴字幕

手把手教你用FUTURE POLICE&#xff1a;会议录音秒变带时间轴字幕 1. 为什么需要高精度字幕对齐&#xff1f; 在日常工作中&#xff0c;我们经常遇到这样的场景&#xff1a;重要会议录音需要整理成文字稿&#xff0c;但人工听写耗时耗力&#xff1b;视频剪辑时需要添加字幕&a…...

仙侠H5手游【九州封魔劫代金券内购版】服务端图文搭建教程(含资源下载+部署过程)

游戏截图搭建环境信息 系统&#xff1a;Centos 7.6 内存&#xff1a;4G 处理器&#xff1a;2核 注意事项 复制代码需要通过浏览器打开文章才不会报错 搭建资源获取 百度网盘&#xff1a;https://pan.baidu.com/s/1wmz7RegQGBaNrYYVbuJqgg?pwdkdn4 解压密码&#xff1a;www.won…...

保姆级教程:用PtitPrince的RainCloud函数,5步搞定分组数据可视化

5步精通RainCloud Plot&#xff1a;用PtitPrince实现专业级分组数据可视化 第一次看到同事用雨云图展示A/B测试结果时&#xff0c;我被这种"既见森林又见树木"的呈现方式震撼了——左侧的密度曲线如山脉般起伏&#xff0c;中间的箱线图标出关键分位点&#xff0c;右侧…...

【office2pdf】PPTX 字体解析与文本样式继承(PPTX_FONT_RESOLUTION.md)

摘要 本文档记录了 PPTX 保真度问题&#xff0c;该问题最初看起来像是布局错误&#xff0c; 但实际上是由不完整的字体和文本样式解析引起的。 可见的症状是多个幻灯片上的文本块&#xff0c;尤其是幻灯片 4 的"SKILLS"区域&#xff0c; 与 PowerPoint 不匹配&#x…...

本地硬盘装系统神器更新!WinToHDD v7.0,支持加密/多分区安装

软件下载 夸克下载&#xff1a;https://pan.quark.cn/s/8bb2d79a1f4c迅雷下载&#xff1a;https://pan.xunlei.com/s/VOottCVsfGa3nDKv07YreMVPA1?pwdve85#UC下载&#xff1a;https://pan.xunlei.com/s/VOottCVsfGa3nDKv07YreMVPA1?pwdve85# 软件介绍 前几天一直看见有群友…...

别只看成功率!拆解AlphaFold3在抗体对接中那60%的失败案例

AlphaFold3抗体对接失败的深层解析&#xff1a;60%案例背后的技术挑战与突破路径 当AlphaFold3&#xff08;AF3&#xff09;在抗体-抗原对接领域取得8.9%的高精度成功率时&#xff0c;科学界为之振奋。但鲜少有人关注到&#xff0c;在单种子采样条件下&#xff0c;这一系统仍有…...

从CPU到内存:用74LS74芯片手把手教你搭建一个D边沿触发器(附波形图分析)

从面包板到示波器&#xff1a;用74LS74芯片实战D边沿触发器的完整指南 当你第一次在数字电路课本上看到"D边沿触发器"这个词时&#xff0c;是否感觉它像是一个抽象的黑盒子&#xff1f;教科书上的真值表和波形图虽然精确&#xff0c;但总缺少那么一点"触手可及&…...

GY39传感器实战:从数据采集到环境监测应用

1. GY39传感器入门指南 第一次拿到GY39传感器时&#xff0c;我完全被它小巧的体积震惊了。这个只有拇指大小的模块&#xff0c;居然能同时测量气压、温湿度、光照强度四种环境参数。它的工作电压是3-5V&#xff0c;用普通的USB充电器就能供电&#xff0c;特别适合DIY项目。 GY3…...

Psins实战:从零解析SINS/GPS松组合导航中的Kalman滤波器初始化与调参

1. 初识SINS/GPS松组合导航与Kalman滤波 刚接触导航算法的朋友可能会被"SINS/GPS松组合"这个术语吓到&#xff0c;其实拆开看很简单。SINS&#xff08;捷联惯性导航系统&#xff09;就像是个不知疲倦的计步器&#xff0c;通过IMU&#xff08;惯性测量单元&#xff09…...

从iptables迁移到nftables:表/链/规则的对照操作指南(含性能对比)

从iptables到nftables的平滑迁移实战指南 在Linux网络安全管理领域&#xff0c;防火墙技术的演进从未停歇。对于已经熟悉iptables的中高级用户而言&#xff0c;nftables的出现既是挑战也是机遇。作为Netfilter项目的新一代防火墙框架&#xff0c;nftables不仅统一了IPv4/IPv6防…...