首页 > 编程语言 >Netty源码学习1——NioEventLoopGroup的初始化

Netty源码学习1——NioEventLoopGroup的初始化

时间:2023-08-13 17:55:51浏览次数:39  
标签:NioEventLoop Netty EventLoop NioEventLoopGroup Selector 源码 Channel

系列文章目录和关于我

零丶引入

netty源码学习中,大家maybe都接触到如下的hello world——netty客户端启动的demo:

image-20230813113511153
映入眼帘的第一个类就是NioEventLoopGroup,很多文章上来就是是Netty中的核心类,啥Channel,Pipeline,Context,Boostrap一通劈里啪啦,我看起来比较费劲。

so本文不会上来就给大家介绍netty中所有的组件,而是先从NioEventLoopGroup入手。

一丶何为NioEventLoopGroup & NioEventLoopGroup继承关系

image-20230813114035210

上图为NioEventLoopGroup继承关系,根据源码上的注释我们可以大概了解这些类的作用:

  • EventExecutorGroup:通过next方法提供EventExecutor,并且还负责处理它们的生命周期,并允许以全局方式关闭它们(指shutdownGracefully关闭EventExecutorGroup中的所有EventExecutor)

    image-20230813114502850

  • EventExecutor:EventExecutor 是一个特殊的 EventExecutorGroup(next方法指只会返回自己),它附带了一些方便的方法来查看线程是否在事件循环中执行。

    image-20230813114734411

  • EventLoopGroup:特殊EventExecutorGroup,允许注册在事件循环期间注册Channel

    Channel是一个连接网络输入和IO处理的桥梁。可以通过Channel来判断当前的状态,是open还是connected,还可以判断当前Channel支持的IO操作。
    

    image-20230813115024609

  • AbstractEventExecutorGroup:EventExecutorGroup的抽象实现,可以看到对AbstractEventExecutorGroup提交任务,最终都会被其使用next获取EventExecutor进行处理。

    EventExecutor是打工仔,AbstractEventExecutorGroup是分配任务的leader

    image-20230813115730045

  • MultithreadEventExecutorGroup:从名字上可以看出这是一个多线程的EventExecutorGroup,其中的“线程” = EventExecutor

    image-20230813120101605

  • MultithreadEventLoopGroup:多线程的EventExecutorGroup + EventLoopGroup = 多线程处理任务且允许Channel注册的EventLoopGroup,下面猫一眼其Channel的注册:

    image-20230813120527154

    可以看到还是交给了打工人EventLoop

  • NioEventLoopGroup:MultithreadEventLoopGroup实现,其关联的Channel是基于NIO Selector的Channel实现的

二丶NioEventLoopGroup的初始化

image-20230813145926579 image-20230813145439465

在Netty 入门Demo中无论是Server还是Client都会先初始化NioEventLoopGroup,下面对于这个初始化过程进行源码解析。

image-20230813150118112

  • 参数中的SelectorProvider 是由SelectorProvider.provider() 提供的,SelectorProvider #openSelector可以创建selector,不同的操作系统这里会拿到不同的SelectorProvider

  • DefaultSelectStrategyFactory.INSTANCE是SelectStrategyFactory的默认实现,其newSelectStrategy会提供DefaultSelectStrategy作为选择策略,这个策略在Netty NioEventLoop#run方法来左右程序的执行(这点后续详细分析)

    image-20230813150523160

最终NioEventLoopGroup的构造方法将调用父类MultithreadEventExecutorGroup的构造方法

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    checkPositive(nThreads, "nThreads");

    if (executor == null) {
        // 初始化executor
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
 
    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            // 创建EventLoop
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
            throw new IllegalStateException("failed to create a child event loop", e);
        } finally {
            // s省略关闭children数组中EventLoop的代码
        }
    }
  
    // 选择器,EventLoopGroup#next方法依赖此选择器选择EventLoop
    chooser = chooserFactory.newChooser(children);
    
    // 此处省略 EventLoopGroup关闭的Future回调
    
    // 只读set
    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

其中比较有意思的是newChild方法,此方法由NioEventLoopGroup进行实现

image-20230813153046662

且NioEventLoopGroup中Executor将作为参数进行使用,目前我尚不知这个Executor的作用,但是可以先看一下ThreadPerTaskExecutor特性

image-20230813153322350

DefaultThreadFactory#newThread如下

image-20230813153452941

可以看到每一个Thread都是FastThreadLocalThread,每一个任务都会包装为FastThreadLocalRunnable#wrap方法包装,以保证FastThreadLocal会在任务执行后进行释放(和TransmittableThreadLocal的做法类似,都是对原有Thread,和任务的包装)

image-20230813153551124

Netty的FastThreadLocal的奥妙后续会单独进行学习和分析。

三丶NioEventLoop

image-20230813152953158

在学习NioEventLoopGroup是如何创建NioEventLoop之前,我们先看下NioEventLoop的继承体现。

  • SingleThreadEventExecutor:单线程Exectuor,内部持有一个Thread,在第一次提交任务的时候会将任务放到任务队列,并启动内部的Thread,该Thread执行逻辑交由子类实现
  • EventLoop:Channel注册到EventLoop中,后续由EventLoop处理这些Channel
  • SingleThreadEventLoop:SingleThreadEventExecutor + EventLoop = 单线程处理任务的EventLoop

四丶NioEventLoopGroup#newChild创建NioEventLoop

image-20230813154819601

值得一说是还是这个executor参数,在NioEventLoop调用父类构造函数的时候,这个executor 会使用ThreadExecutorMap.apply包装一下,会将当前的NioEventLoop记录到FastThreadLocal中,让任务的执行过程可以从FastThreadLocal中拿到当前NioEventLoop

image-20230813155111719

1.Netty对于Selector的优化

image-20230813155853959

在NioEventLoop的创建时,会执行openSelector返回一对Selector,其中一个时原生的JDK中的Selector,另外一个时Netty优化的Selector,我们看下Netty做了什么优化

image-20230813160242297

可以看到Netty会反射修该原JDK中的Selector 的selectedKeys和publicSelectedKeys字段,在原生JDK Selector中这两个Set的作用:

  • selectedKeys:Selector会将自己监听到的IO就绪Channel放到selectedKeys

  • publicSelectedKeys:selectedKeys 的视图,用于向外部线程返回IO就绪SelectionKey。这个集合在外部线程中只能做删除操作不可增加元素,并且`不是线程安

    全的

Netty为啥要进行优化昵

  • SelectorImpl监听到IO就绪SelectionKey 后,会将就绪IO对应的`SelectionKey add到selectedKeys集合中

  • IO就绪后会唤醒阻塞在SelectorImpl#select方法上的线程,这些线程将调用SelectorImpl#selectedKeys,进行遍历处理,这里将返回publicSelectedKeys

    image-20230813161929646

这里的add和遍历操作都是针对set(HashSet)的,HashSet底层基于HashMap存在Hash冲突导致其插入需要使用拉链法,遍历的时候也无法使用 CPU 缓存的优势来提高遍历的效率(指数组元素都在紧密排布大概率在一个缓存行,可以利用预读优势)因此Netty将其优化为自己实现的SelectedSelectionKeySet

image-20230813162459675

最终会将JDK元素的Selector包装为SelectedSelectionKeySetSelector

image-20230813162653039

可以看到每次执行select方法会对selectedKeys进行处理,其实就是清除原生JDK Selector中的selectedKeys字段和publicSelectedKeys中的SelectionKey,为什么要这么做昵,让我们回顾原生JDK Selector的使用

image-20230813162927085

在selector#selectedKeys读取到publicSelectedKeys中就绪的就绪的IO实现后,会进行处理,处理完成后会进行clear方法清除已经处理的SelectionKey,但是Netty优化后的SelectedSelectionKeySet是不支持remove的,而是在SelectedSelectionKeySetSelector下一次select的时候进行批量清除。

image-20230813163217029

2.NioEventLoop任务队列的创建Mpsc队列

image-20230813163840474 image-20230813164108541

最终创建的MpscUnboundedArrayQueue或者MpscUnboundedAtomicArrayQueue,Mpsc是Multiple producers and single consumers 多生产者单消费者的缩写,它支持多个生产者多个消费者的情况并且性能强劲(cas无锁设计减少了频繁的唤醒和阻塞,内部使用数组在需要扩容的时候不是复制旧数组,而是数组尾部元素指向下一个数组)

3.NioEventLoop中的任务队列

image-20230813172617175

事件循环中存在的事件队列,在NioEventLoop中的体现就是三个任务队列

  • taskQueue:就是上面提到的Mpsc队列,NioEventLoop中的线程run会处理其中的任务
  • tailTaskQueue:尾部队列,处理完taskQueue中所有任务后,会获取tailTaskQueue中的任务进行执行
  • scheduledTaskQueue:PriorityQueue<ScheduledFutureTask<?>>默认是基于数组的堆,用来存在延时任务

五丶总结

学习了NioEventLoopGroup 和 NioEventLoop的初始化,后续NioEventLoop 事件循环中的循环进行源码分析。

标签:NioEventLoop,Netty,EventLoop,NioEventLoopGroup,Selector,源码,Channel
From: https://www.cnblogs.com/cuzzz/p/17626904.html

相关文章

  • 【Java】智慧工地管理平台源码
    智慧工地是聚焦工程施工现场,紧紧围绕人、机、料、法、环等关键要素,综合运用物联网、云计算、大数据、移动计算和智能设备等软硬件信息技术,与施工生产过程相融合。一、什么是智慧工地智慧工地是指利用移动互联、物联网、智能算法、地理信息系统、大数据挖掘分析等信息技术,提高项目......
  • Java| jdk的src源码目录讲解
    JavaJDK的源代码目录(src)包含了Java核心类库的源代码,它提供了Java编程语言的基本功能和类。src目录结构通常按照包的层次结构组织,每个包对应一个文件夹,而每个类则在相应的包文件夹中以.java文件的形式存在。目录结构-com--sun-java--applet--awt--beans--io--lang--mat......
  • json解析源码学习
    c#的几个Json库MiniJSONSimpleJsonlitjsonNewtonJson 其中MiniJSON最简单,所以这边也是学习这个库的Json解析部分(注意:只涉及解析,没有生成json)。整体代码也没有用到特别的算法什么的,就是一个一个字符的读取,然后根据读到的边界标识符来进行后续的读取,边界标识符的话就是:{}、[......
  • 知识付费系统源码独立部署版,小鹅通源码平替
    兔知课堂是专注于知识付费领域的应用。支持图文、音频、视频、直播等内容形式,实现内容产品化,可以把多个内容打包成专栏等形式,建立体系化的内容产品,满足系统学习需求。用户可以随时随地观看自己感兴趣的内容。 后台提供页面DIY,管理员可以自主搭建个性化知识店铺页面的功能。微页......
  • requests源码阅读笔记
    requests框架结构整个架构包括两部分:Session持久化参数和HTTPAdapter适配器连接请求,其余部分都是urllib3的内容。......
  • 来自开源社区的最大事件--- IBM收购红帽RHEL后终止提供免费的软件源和操作系统源码
    保持Linux的开放性和自由性--我们不能不这样做作者:首席企业架构师EdwardScreven和OracleLinux开发主管WimCoekaerts-2023年7月10日甲骨文加入Linux社区已有25年。这些年来,我们的目标始终如一:帮助Linux成为人人都能免费使用的最佳服务器操作系统,并为有需要的用户......
  • RT-Thead学习-GD32移植(基于RT-Thread Nano源码)
    1前言当前关于RT的移植教程有很多,纯复制大佬们的很迷糊,参考官方手册和一些经验贴,完成了基于Nano源码的移植,最简单的移植教程就是基于keil的和这一种。参考资料1.野火资料(https://doc.embedfire.com/rtos/rtthread/zh/latest/application/porting_to_stm32.html)2.微信公众号(物联网......
  • openGauss数据库源码解析系列文章——安全管理源码解析(三)
    Gauss松鼠会[openGauss](javascript:void(0);)2023-07-2917:58发表于四川在上篇openGauss数据库源码解析系列文章——安全管理源码解析(一)我们围绕安全管理整体架构和代码概览、安全认证原理介绍和代码解析进行了简单介绍。本篇将继续角色管理、对象权限管理的学习,全文阅读需要3......
  • openGauss数据库源码解析系列文章——安全管理源码解析(四)
    四、对象权限管理权限管理是安全管理重要的一环,openGauss权限管理基于访问控制列表(accesscontrollist,ACL)实现。4.1权限管理1.访问控制列表访问控制列表是实现数据库对象权限管理的基础,每个对象都具有ACL,存储该对象的所有授权信息。当用户访问对象时,只有用户在对象的ACL中并且......
  • 国标GB28181视频平台LntonGBS(源码版)国标视频平台在网络不稳定的强况下重复申请视频拉
    LntonGBS是基于国标GB28181协议的视频云服务平台,支持将国标协议的设备统一接入并进行集中管理。平台具备优秀的视频能力,包括视频监控直播、录像、云存储、回放、平台级联、语音对讲、智能告警等功能,在线下场景中已有大量落地应用。我们在项目测试中发现,LntonGBS通过web页面请求拉流......