• 源码解析-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

  • 相关阅读:
    常见名词解释
    主板结构解析
    计算机网络原理的总结
    Nginx的介绍
    优雅的python
    python小技巧
    python列表小程序
    学会浏览器查东西
    列表推导式
    深度优先算法与广度优先算法
  • 原文地址:https://www.cnblogs.com/ttaall/p/14201190.html
Copyright © 2020-2023  润新知