源码解析-Netty源码之EventLoopGroup线程池分析

 从Netty官方给出的example包着手点分析,echo 回声,也就是客户端传什么,服务端传回什么

 

先从客户端开始看

 

属性,ip地址,端口号,数据大小之类的 四个写死了的

 判断ssl是否为空,来决定是否需要初始化SslContext (可以理解为一些客户端的初始化配置,我们的例子中默认是null)

下面重点来了,前面暂时都可以忽略

核心点几步:

1. 创建一个线程池组group;  //初始化线程池系列 包括group loop的初始化,配置了chooser,以及引用EventExecutor代表loop

2. 创建一个客户端Bootstrap;

3. 加入线程池组,

4. 加入socketChannel,     //创建channelFactory 将来用来创建NioSocketChannel

5. 定义了ChannelInitializer,重写initChannel()方法,调用Channel的pipeline,加入了head,tail和自定义的handel;

6. 异步connect;       //这一步做的事比较多,new一个channel,初始化一个pipeline,将ChannelInitializer内部的handler全部加入到新的pipeline。从group中拿一个loop 将channel注入到loop中,启动loop线程,将channel注册到selector上

7. 关闭channel;

8. 关闭线程池组group;

 

这一篇重点分析第一步 创建线程池组group 与线程loop

注意 这里的group与loop的概念区别于jdk的线程池与线程,更像是一种公司与打工人的关系,公司负责提供打工人,打工人负责具体做事情。

loop打工人用来处理连接的生命周期中所发生的事件,在内部,将会为每个Channel分配一个loop。

group 是一个loop 池,包含很多的loop,需要接活的时候直接让公司派出一个打工人来干活

Netty 为每个 Channel 分配了一个 loop,用于处理用户连接请求、对用户请求的处理等所有事件。

一个 Channel 一旦与一个 loop 相绑定,那么在 Channel 的整个生命周期内是不能改变的。一个 loop 可以与多个 Channel 绑定。即 Channel 与 EventLoop 的关系是 n:1的关系

---

 

 继承结构

 前面所说的loop和group 落地的实现就是NioEventLoop和NioEventLoopGroup

从继承关系可以看出,无论是loop还是group都继承自 jdk的Executor,都具有最原始的execute()方法

最顶层Executor,然后是线程池,然后时可以定时处理的线程池

loop继承了SingleThreadEventLoop,看名字  这是个单线程的线程池,也就是说 loop内部其实放着一个只有一个线程的线程池,具体干活就靠着一个线程了

group继承子MultithreadEventLoopGroup.多种线程事件的线程池,与其说是线程池,不如说这是一个loop池,存放了各种各样的loop,还具有一些找哪个loop出来干活的方法

 

源码分析

从这里为入口开始探索

 

 先给个线程数为0,继续

 

 先给个executor为null,继续

 

 先给个默认的selectorProvider,用来创建selector监听端口号,这个provider方法由jdk1.4提供

 

 给个默认的选择策略

 

 给个默认的拒绝策略 (直接抛异常)

 到了父类的构造器了,线程数如果是0,直接给个默认大小 (cpu核心数*2)  这里的args参数就是什么选择策略啊 拒绝策略啊 不是本篇重点,继续往下走

 初始化了个DefaultEventExecutorChooserFactory,这东西就是用来选择loop的,也就是有需求的时候 他会从从group里选出一个loop 交给channel

 

 

 

1.MuiltithreadEventExecutorGroup() 构造方法   完成了所有打工人loop的初始化过程

 来到重点了,重要的初始化过程都在这进行  这一个方法被我拆成了几个部分分别介绍

 首先判断一下线程数,也是loop数,刚才给了 cpu核心数*2

判断executor是否为null,如果为null创建一个,刚才一直没给,会在这里new一个

executor的作用就是来一个任务 就创建一个线程。  这个executor是给loop用的,其实也可以理解成他就是loop内部的线程池,后面我们会说到 这个线程池只有一个线程

children 是什么呢,一个EventExecutor数组,在这new了一个线程数大小的数组,猜一下 这个EventExecutor是loop的一种形式,或者说他本身就是loop,存在于group中的数组

 

for(线程数次循环){  //循环初始化每一个child,也就是每一个loop

   try{

     newChild(executor,args);    //创建一个child    这里声明一下说法关系:  NioEventLoopGroup == group == parent == 线程池 == 公司     NioEventLoop == loop == child == 线程 == 打工人

    }catch{}

    finally{

      如果初始化每一个child的过程中有一个失败了,就将所有的child关闭;

    }

}

用传过来的chooserFactory创建一个chooser,将children传进去,chooser的next()方法 用来选择loop去干活的

初始化FutureListener,将这个listener注册给每一个loop,用来监控loop活干的怎么样。打工人心累

设置readonlyChildren集合,目前是所有loop。

 

 

 2. new child(executor,args)  创建一个打工人loop的过程

NioEventLoop中落地的 

创建一个taskQueue任务队列,要做的任务都会存在这 供打工人做任务。这里直接从args参数里提取的

 将之前初始化的参数直接赋过来, 

开启了selector

 

追到这里 super(parent) 这个parent也就是group,每一个loop也存了一下group的引用

其他的都是直接赋值,这里看一下executor怎么玩的

 

传入executor,传入 刚才的SingleThreadEventExecutor,执行传入的任务,交给下面的apply()执行

command.run()  

结合之前说的,executor可以看作loop内部的一个线程池,当来了一个任务,executor直接去执行任务

 

3. newChooser()   

 

 两种创建chooser的策略,根据线程的数量是不是2的倍数来判断的

 

 两种chooser的next()方法不同, 也就是挑选loop打工者的方式不同

 

 

 

 到这里左边这一大坨线程池系列就算理清楚了

 

 

第一篇 源码解析-Netty源码之EventLoopGroup线程池分析

第二篇    源码解析-Netty源码之Bootstrap创建,初始化Channel,Pipeline,handler

posted @ 2020-12-28 13:54  六小扛把子  阅读(727)  评论(0编辑  收藏  举报