Netty源码分析--Channel注册(上)(五)

其实在将这一节之前,我们来分析一个东西,方便下面的工作好开展。

打开启动类,最开始的时候创建了一个NioEventLoopGroup 事件循环组,我们来跟一下这个。

这里bossGroup, 我传入了一个线程, workerGroup 没有入参,默认0, 也就是说父级我用一个线程来处理客户端的接入, 多个线程来处理客户端的读写操作

通过一连串的构造方法进入到

executor 是 null , selectorProvider 用来创建一个多路复用器, 最后一个参数传入了 一个多路复用器的一个策略, 这个策略我们后面会讲到

紧接着又传入了一个拒绝策略, 由于  NioEventLoopGroup 继承了 MultithreadEventLoopGroup,所以进入 MultithreadEventLoopGroup 的构造方法

这里的判断, bossGroup 传入了 1 ,所以nThreads = 1;  workerGroup 没有入参,默认是0 ,所以这里到了一个判断默认线程数的地方。

就是这了, NettyRuntime.availableProcessors() * 2  代表 CPU核心数 * 2(处理器超线程数) * 2的值  或  CPU数 * 2的值

在cmd命令中输入“wmic”,然后在出现的新窗口中输入“cpu get *”。
     NumberOfCores:表示CPU核心数
     NumberOfLogicalProcessors:表示CPU线程数

所以我这里是 CPU核心数 是 2 , CPU 线程数 是 4 , 所以我的 NettyRuntime.availableProcessors() * 2 = 8, 然后这里Math.max(1,8) 取大的 就是 8 了

所以我的 workerGroup 线程数 是 8

好了,我们继续跟进去

这里创建了一个线程工厂,主要是为线程设置名字、是否守护进程、线程的优先级等等。然后创建一个任务执行器,把线程工厂传进去赋给成员变量

这个executor 后面在每个 事件执行器 创建子线程处理task来用

接下来创建一个长度是nThreads的 EventExecutor[]  ,对于 子事件循环组来说,这里其实是创建了一个长度为8 的NioEventLoop的数组, 即 EventExecutor[]  children = new NioEventLoop[8]

这个地方我纠结了一下,因为我语文太差了,我决定画个图来展示一下, 虽然我美术也不好。

大家看下这个代码层级结构, 其实就很显而易见了,这里我要说一下,SingleThreadEventExecutor 中有一个thread成员变量,说明每个都只有一个线程来处理,并且含有任务队列和任务的执行器。

另外每一个NioEventLoop都含有一个selector 多路复用器 。

继续看,这里通过默认的选择工厂来创建一个选择器。

跟进去,我们看到下面的这段代码,不得不感叹Netty真的已经把性能发挥到了极致,能用位运算的绝不会用数学计算法,所以这里对选择器进行了区分

看这个判断方法,意思是 如果是 2的次方 ,那么创建一个 PowerOfTwoEventExecutorChooser 选择器

我特意去验证了一个这个算法

结果是:

好了,讲到这里我们可以重新开始讲注册了,仍然进入  initAndRegister()方法

config().group() 这个获取到的 是 ServerBootStrap.group(), 那么也就是取到了 父级的事件循环组 也就是bossGroup

根据我上上图的分析,那么注册方法进入的肯定是 MultithreadEventLoopGroup

这里有一个next()方法,用来选择一个NioEventLoop。由于我这里是1个线程的数组,所以进入

因此,对于bossGroup来说,就是 0 & 0 = 0   1 & 0 = 0  2 & 0 = 0 .....

对于workerGroup来说,就是 0 & 7 = 0 1 & 7 = 1  2 & 7 = 2 .... 8 & 7 = 0  9 & 7 = 1 ....

所以这里其实是一个轮询的算法。

ok, 看到这里我们猜测是 从   new NioEventLoop[1]  中轮询一个 NioEventLoop, 然后把channel注册到上面的多路复用器上。

继续看, 根据那个流程图,可以推断出是进入到  SingleThreadEventLoop

接着传入了一个 DefaultChannelPromise ,用来做注册结果的异步通知的。传入了channel 和 当前的这个 SingleThreadEventLoop ,当然具体怎么异步通知的,我们后面会讲到

继续看

这里把刚刚选择出来的 NioEventLoop 赋给 Channel 的 eventLoop 的成员变量, 这里也就意味着 ,这个NioEventLoop 也将一直伴随 这个channel 的所有的读写操作, 因为通过上面的那个流程图表明了 一个NioEventLoop 上面只有一个Thread , 那么也可以得出 一个Channel 整个周期 内所有的读写操作,全部由同一个Thread来完成, 这也就说明了为什么Netty没有线程安全问题,当然随着后面的讲解,你将会对这个地方理解的更加的深刻。

好了,注册的内容我们下一节接着说。

原文地址:https://www.cnblogs.com/huxipeng/p/10994455.html

时间: 2024-08-29 22:33:39

Netty源码分析--Channel注册(上)(五)的相关文章

Netty源码分析--Channel注册(中)(六)

接上一篇,我们继续看 不知道大家第一次看这段代码的时候有没有一脸懵逼,反正我是一脸懵,为什么这个if else 最终都是调用的register0方法,都是一样的. 其实这里就是为什么Netty是线程安全的根本原因. 我们先看下 eventLoop.inEventLoop() 方法 第一张图传入了 当前的 线程, 第二个图 判断了 当前这个NioEventLoop中的Thread 是不是和当前线程相等, 如果相等返回true, 相反就是false. 我们debug 看一下 发现NioEventLo

Netty源码分析-- 处理客户端接入请求(八)

这一节我们来一起看下,一个客户端接入进来是什么情况.首先我们根据之前的分析,先启动服务端,然后打一个断点. 这个断点打在哪里呢?就是NioEventLoop上的select方法上. 然后我们启动一个客户端. 然后我们debug看到,selectedKey的数量 = 1,说明有accept或者读写等事件发生. 接下就会进 processSelectedKeys() 我们上一节讲到,这里的attach就是NioServerSocketChannel, 我们进入 processSelectedKey(

4. Netty源码分析之Unsafe

Unsafe类实际上是Channel接口的辅助类,实际的IO操作都是由Unsafe接口完成的. 一.Unsafe继承关系图 二.AbstractUnsafe源码分析 1. register方法 register方法主要用于将当前Unsafe对应的Channel注册到EventLoop的多路复用器上,然后调用DefaultChannelPipeline的fireChannelRegisted方法,如果Channel被激活,则调用fireChannelActive方法. public final v

Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector

Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void read() { //必须是NioEventLoop方法调用的, 不能通过外部线程调用 assert eventLoop().inEventLoop(); //服务端channel的config final ChannelConfig config = config(); //服务端channel

Netty源码分析--内存模型(上)(十一)

前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们来一起看下客户端接入完成之后,是怎么实现读写操作的?我们自己想一下,应该就是为刚刚读取的数据分配一块缓冲区,然后把channel中的信息写入到缓冲区中,然后传入到各个handler链上,分别进行处理.那Netty是怎么去分配一块缓冲区的呢?这个就涉及到了Netty的内存模型. 当然,我们在第一节的时

netty 源码分析二

以服务端启动,接收客户端连接整个过程为例分析, 简略分为 五个过程: 1.NioServerSocketChannel 管道生成, 2.NioServerSocketChannel 管道完成初始化, 3.NioServerSocketChannel注册至Selector选择器, 4.NioServerSocketChannel管道绑定到指定端口,启动服务 5.NioServerSocketChannel接受客户端的连接,进行相应IO操作 Ps:netty内部过程远比这复杂,简略记录下方便以后回忆

Netty源码分析第3章(客户端接入流程)---->第5节: 监听读事件

Netty源码分析第三章: 客户端接入流程 第五节: 监听读事件 我们回到AbstractUnsafe的register0()方法: private void register0(ChannelPromise promise) { try { //省略代码 //做实际的注册 doRegister(); neverRegistered = false; registered = true; //触发事件 pipeline.invokeHandlerAddedIfNeeded(); safeSetS

netty源码分析之揭开reactor线程的面纱(二)

如果你对netty的reactor线程不了解,建议先看下上一篇文章netty源码分析之揭开reactor线程的面纱(一),这里再把reactor中的三个步骤的图贴一下 reactor线程 我们已经了解到netty reactor线程的第一步是轮询出注册在selector上面的IO事件(select),那么接下来就要处理这些IO事件(process selected keys),本篇文章我们将一起来探讨netty处理IO事件的细节 我们进入到reactor线程的 run 方法,找到处理IO事件的代

Netty源码分析第2章(NioEventLoop)---->第7节: 处理IO事件

Netty源码分析第二章: NioEventLoop 第七节:处理IO事件 上一小节我们了解了执行select()操作的相关逻辑, 这一小节我们继续学习select()之后, 轮询到io事件的相关逻辑: 回到NioEventLoop的run()方法: protected void run() { for (;;) { try { switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) { case Sele