【Netty】(3)—源码NioEventLoopGroup

一、概念
NioEventLoopGroup对象可以理解为一个线程池,内部维护了一组线程,每个线程负责处理多个Channel上的事件,而一个Channel只对应于一个线程,这样可以回避多线程下的数据同步问题。

我们先回顾下 上篇博客的服务器代码

    // 定义一对线程组
    // 主线程组, 用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    // 从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    
    // netty服务器的创建, 辅助工具类,用于服务器通道的一系列配置
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(bossGroup, workerGroup)           //绑定两个线程组               
        //省略......

职责:

作为服务端 Acceptor 线程,负责处理客户端的请求接入。
作为客户端 Connector 线程,负责注册监听连接操作位,用于判断异步连接结果。
作为IO 线程,监听网络读操作位,负责从 SocketChannel 中读取报文。
作为 IO 线程,负责向 SocketChannel 写入报文发送给对方,如果发生写半包,会自动注册监听写事件,用 于后续继续发送半包数据,直到数据全部发送完成。
作为定时任务线程,可以执行定时任务,例如链路空闲检测和发送心跳消息等。
作为线程执行器可以执行普通的任务线程(Runnable)。

二、NioEventLoopGroup源码分析
上面的代码 创建bossGroup及workerGroup时,使用了NioEventLoopGroup的无参构造方法,本篇将从此无参构造入手,详细分析NioEventLoopGroup的初始化过程。

/**
 * 1、首先我们看看NioEventLoopGroup的无参构造方法:
 * 作用:线程数为0
 */
public NioEventLoopGroup() {
    this(0);
}

/**
 * 2、继续调用构造函数。
 * 作用:指定线程为0,且Executor为null
 */
public NioEventLoopGroup(int nThreads) {
    this(nThreads, (Executor) null);
}

/**
 * 3、继续调用构造函数
 * 作用:此构造方法它会指定selector的辅助类 "SelectorProvider.provider()"
 */
public NioEventLoopGroup(int nThreads, Executor executor) {
    this(nThreads, executor, SelectorProvider.provider());
}

/**
 * 4、继续调用构造函数
 * 作用:初始化了一个默认的选择策略工厂,用于生成select策略
 */
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {
    this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}

/**
 * 5、继续调用构造函数
 * 作用:指定拒绝策略:RejectedExecutionHandlers.reject()
 */
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {
    super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}

经过上面一系列的构造方法调用,此时参数值对应如下:

​ nThreads: 0
​ executor: null
​ selectorProvider: SelectorProvider.provider()
​ selectStrategyFactory: DefaultSelectStrategyFactory.INSTANCE
​ 以及指定了拒绝策略: RejectedExecutionHandlers.reject()
/**
* 6、从这里开始 调用父类 MultithreadEventLoopGroup 的构造函数
* 作用: 就是当指定的线程数为0时,使用默认的线程数DEFAULT_EVENT_LOOP_THREADS,
* 而DEFAULT_EVENT_LOOP_THREAD是在静态代码块中就被执行。
*/
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object… args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

/**
 * 6.1 我们看下静态代码块
 * 作用:到这一步得出关键的一点:`如果初始化NioEventLoopGroup未指定线程数,默认是CPU核心数*2`。
 */
private static final int DEFAULT_EVENT_LOOP_THREADS;

static {
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2))
}

/**
 * 7、继续调用父类 MultithreadEventLoopGroup 构造函数
 * 作用:指定了一个EventExecutor的选择工厂DefaultEventExecutorChooserFactory,
 *      此工厂主要是用于选择下一个可用的[EventExecutor](http://zzsffx.1358tv.com.com)
 */
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}

/**
 * 8、继续调用父类 MultithreadEventLoopGroup 构造函数 这里就是核心代码 删除部分非核心代码
 *    作用单独分析
 */
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {

    //1、
    //executor校验非空, 如果为空就创建ThreadPerTaskExecutor, 该类实现了 Executor接口
    // 这个executor 是用来执行线程池中的所有的线程,也就是所有的NioEventLoop,其实从
    //NioEventLoop构造器中也可以知道,NioEventLoop构造器中都传入了executor这个参数。
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    //2、
    //这里的children数组, 其实就是线程池的核心实现,线程池中就是通过指定的线程数组来实现线程池;
    //数组中每个元素其实就是一个EventLoop,EventLoop是EventExecutor的子接口。
    children = new EventExecutor[nThreads];

    //for循环实例化children数组,NioEventLoop对象
    for (int i = 0; i < nThreads; i++) {
        boolean success = false;

        //3、
        //newChild(executor, args) 函数在NioEventLoopGroup类中实现了,
        // 实质就是就是存入了一个 NIOEventLoop类实例
        children[i] = newChild(executor, args);
        success = true;
    }

    //4、实例化线程工厂执行器选择器: 根据children获取选择器
    chooser = chooserFactory.newChooser(children);

    //5、为每个EventLoop线程添加 线程终止监听器
    final FutureListener<Object> terminationListener = new FutureListener<Object>() {
        @Override
        public void operationComplete(Future<Object> future) throws Exception {
            if (terminatedChildren.incrementAndGet() == children.length) {
                terminationFuture.setSuccess(null);
            }
        }
    };

    //6、将children 添加到对应的set集合中去重, 表示只可读。
    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet([childrenSet](http://yaoqikj.com));
}

}

/**

  • 8.3.1 我们再来看下 newChild(executor, args) 里的方法
  • 我们可以看到 返回的就是一个 NioEventLoop
    */
    @Override
    protected EventLoop newChild(Executor executor, Object… args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
    ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }
    我们再回顾总结一下:

NioEventLoopGroup初始化时未指定线程数,那么会使用默认线程数,即 线程数 = CPU核心数 * 2;
每个NioEventLoopGroup对象内部都有一组可执行的NioEventLoop数组,一个NIOEventLoop可以理解成就是一个线程。
所有的NIOEventLoop线程是使用相同的 executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler以及是属于某一个NIOEventLoopGroup的。
这一点从 newChild(executor, args); 方法就可以看出:newChild()的实现是在NIOEventLoopGroup中实现的。
当有IO事件来时,需要从线程池中选择一个线程出来执行,这时候的NioEventLoop选择策略是由GenericEventExecutorChooser实现的, 并调用该类的next()方法。
每个NioEventLoopGroup对象都有一个NioEventLoop选择器与之对应,其会根据NioEventLoop的个数,动态选择chooser(如果是2的幂次方,则按位运算,否则使用普通的轮询)
所以通过上面的分析,我们得出NioEventLoopGroup主要功能就是为了创建一定数量的NioEventLoop,而真正的重点就在NioEventLoop中,它是整个netty线程执行的关键。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值