为了账号安全,请及时绑定邮箱和手机立即绑定

Netty源码-Reactor线程模型之NioEventLoopGroup研究

标签:
Java 源码

Netty源码-Reactor线程模型之NioEventLoopGroup研究

在Netty网络编程中,不管是编写客户端代码,还是编写服务端代码,我们都写过这段代码

EventLoopGroup bossGroup = new NioEventLoopGroup(1);

在Netty的线程模型中,NioEventLoopGroup扮演着线程池角色,一说到线程池,不由自主地地会想到ThreadPoolExecutor,NioEventLoopGroup是不是对ThreadPoolExecutor的二次封装了下,我们从下面几个方向来分析。

https://img1.sycdn.imooc.com/60d29cb000018aae11920410.jpg

NioEventLoopGroup源码分析思维导图

初始化时做了什么?

https://img3.sycdn.imooc.com/60d29cb00001b9b608840754.jpg

NioEventLoopGroup层次结构图

从层次结构图上可以看出,NioEventLoopGroup实现了线程池模型的ScheduledExecutorService,ExecutorService和Executor接口,因此可知NioEventLoopGroup提供了线程池相关功能。

NioEventLoopGroup继承了MultithreadEventLoopGroup,父类MultithreadEventLoopGroup继承了抽象类
MultithreadEventExecutorGroup,其抽象方法newChild由子类实现,使用了模板设计模式,一步一步跟踪,最终定位到
MultithreadEventExecutorGroup的构造函数,初始化时做了3件事:

  • 创建线程工厂并构建线程执行器Executor

  • 创建指定数量nThreads的children线程组并初始化

  • 创建线程选择器chooser

创建线程工厂并构建线程执行器Executor:如果我们没有传executor,会帮忙创建一个线程工厂DefaultThreadFactory来构建线程执行器ThreadPerTaskExecutor。DefaultThreadFactory会创建FastThreadLocalThread线程来执行任务。

if (executor == null) {
  executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}

创建指定数量nThreads的children线程组并初始化:第一步创建指定数量nThreads的线程组。nThreads没有传或者传0,会默认指定2*cpu线程数。

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {  //nThreads为0时,会设置2*cpu线程数
  super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}static {  //默认线程数:2*cpu
  DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(    "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));  if (logger.isDebugEnabled()) {
    logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
  }
}

第二步通过newChild()方法初始化children线程组,newChild()是个抽象方法,具体实现由子类NioEventLoopGroup完成。在newChild()方法中,NioEventLoop的初始化参数有6个:

  • 参数parent:NioEventLoopGroup线程组本身;

  • 参数executor:线程执行器,用于启动线程,在SingleThreadEventExecutor的doStartThread()方法中被调用;

  • 参数selectorProvider:NIO的Selector选择器的提供者;

  • 参数strategy:主要在NioEventLoop的run()方法中用于控制选择循环;

  • 参数rejectedExecutionHandler:非I/O任务提交被拒绝时的处理Handler;

  • 参数queueFactory:队列工厂,在NioEventLoop中,队列读是单线程操作,而队列写则可能是多线程操作,使用支持多生产者、单消费者的队列比较合适,默认为MpscChunkedArrayQueue队列。

//指定数量nThreads的children线程组children = new EventExecutor[nThreads];for (int i = 0; i < nThreads; i ++) {  boolean success = false;  try {    // 初始化线程组children,调用子类NioEventLoopGroup的newChild()
    children[i] = newChild(executor, args);
    success = true;
  } catch (Exception e) {    // TODO: Think about if this is a good exception type
    throw new IllegalStateException("failed to create a child event loop", e);
  } 
......
}

创建线程选择器chooser:默认用的是
DefaultEventExecutorChooserFactory.INSTANCE,根据children线程组数量决定使用不同的选择器,使用了单例模式和工厂模式。

chooser = chooserFactory.newChooser(children);

与Java线程池的区别?

  • java线程池的队列是所有线程共享的,NioEventLoopGroup线程组的队列是每个线程独享的。

  • NioEventLoopGroup线程组没有最大线程数的概念。

  • NioEventLoopGroup线程组有线程选择器chooser的概念。

https://img1.sycdn.imooc.com/60d29cb100019b5505860274.jpg

NioEventLoopGroup线程组图

用到了哪些设计模式

  • 模板方法模式:MultithreadEventExecutorGroup类中抽象方法newChild()

  • 工厂模式:DefaultSelectStrategyFactory,DefaultEventExecutorChooserFactory

  • 单例模式:DefaultSelectStrategyFactory,DefaultSelectStrategy,DefaultEventExecutorChooserFactory

  • 策略模式:DefaultSelectStrategy

学到了什么?

  • 用上面的设计模式,可以把变化的地方变得容易扩展

  • Java定义好的线程池模型接口,我们可以重用,还可以根据自己的业务特征实现自己的线程池

    • 接口EventExecutorGroup重写父类接口ExecutorService,为了提示作用

    • NioEventLoopGroup中for (EventExecutor e: this),for-each只能适用于数组或者实现Iterable接口。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1
获赞与收藏
0

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消