一、NioEventLoop的概述

NioEventLoop做为Netty线程模型的核心部分,从本质上讲是一个事件循环执行器,每个NioEventLoop都会绑定一个对应的线程通过一个for(;;)循环来处理与 Channel 相关的 IO 操作, 包括 调用 select 等待就绪的 IO 事件、读写数据与数据的处理等;其次作为任务队列, 执行 taskQueue 中的任务, 例如eventLoop.schedule 提交的定时任务也是这个线程执行的。而NioEventLoopGroup顾名思义,它是维护了一组这样的事件循环器,这也是Netty基于Reactor模式的具体设计体现。

接下来我们就结合具体的代码,对NioEventLoop的整个创建流程进行一个说明与总结

二、NioEventLoop的创建

我们基于Netty构建服务端还是客户端时,都首先需要创建NioEventLoopGroup 实例

  1. // Configure the server.
  2. EventLoopGroup bossGroup = new NioEventLoopGroup();
  3. EventLoopGroup workerGroup = new NioEventLoopGroup();

NioEventLoopGroup 做为基于NIO的处理channle相关IO操作的事件循环器组,它的类层次结构如下

通过NioEventLoopGroup构造函数传入线程数量

  1. /**
  2. * Create a new instance using the specified number of threads, {@link ThreadFactory} and the
  3. * {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
  4. */
  5. public NioEventLoopGroup(int nThreads) {
  6. this(nThreads, (Executor) null);
  7. }

NioEventLoopGroup最终的构造函数中会包含以下几个函数

1、nThreads:传入的线程数量

2、executor :线程执行器Executor接口,默认为空

3、selectorProvider:用于创建Selector的SelectorProvider

4、selectStrategyFactory:传入DefaultSelectStrategyFactory.INSTANCE,  一个使用默认选择策略的工厂。

5、RejectedExecutionHandlers.reject():Netty自定义线程拒绝策略

  1. public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
  2. final SelectStrategyFactory selectStrategyFactory) {
  3. super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
  4. }

在父类MultithreadEventLoopGroup中,会根据你传入nThreads大小,确定初始化的线程数量,为0且没有设置io.netty.eventLoopThreads参数项,则会以当前系统的核心线程数*2做为默认的线程数量

  1. static {
  2. //如果没有设置io.netty.eventLoopThreads参数项,则会以当前运行系统的核心线程数*2作为线程数
  3. DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
  4. "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
  5.  
  6. if (logger.isDebugEnabled()) {
  7. logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
  8. }
  9. }
  10.  
  11. /**
  12. * @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, Object...)
  13. */
  14. protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
  15. super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
  16. }

接下来在MultithreadEventExecutorGroup的构造函数中我们会根据传入的线程数,去初始化和创建一组NioEventLoop

首先我们看下NioEventLoop的类层次结构

下面在MultithreadEventExecutorGroup构造函数中主要完成以下几个功能:

1、初始化ThreadPerTaskExecutor线程执行器,并传入一个线程创建工厂,用于NioEventLoop对应线程的创建

2、根据传入的线程数,初始化一个EventExecutor数组,用于放置创建的NioEventLoop对象

3、循环数组,通过newChild方法创建NioEventLoop对象。

  1. /**
  2. * Create a new instance.
  3. *
  4. * @param nThreads the number of threads that will be used by this instance.
  5. * @param executor the Executor to use, or {@code null} if the default should be used.
  6. * @param chooserFactory the {@link EventExecutorChooserFactory} to use.
  7. * @param args arguments which will passed to each {@link #newChild(Executor, Object...)} call
  8. */
  9. protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
  10. EventExecutorChooserFactory chooserFactory, Object... args) {
  11. if (nThreads <= 0) {
  12. throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
  13. }
  14.  
  15. if (executor == null) {
  16. // 创建线程工厂,netty根据需要指定了线程的命名方式、优先级、是否是守护线程等属性
  17. // 该线程池没有任何队列,提交任务后,创建任何线程类型都是 FastThreadLocalRunnable, 并且立即start。
  18. executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
  19. }
  20. //初始化一组事件循环执行器
  21. children = new EventExecutor[nThreads];
  22.  
  23. //根据传入的线程数,初始化一个线程数组
  24. for (int i = 0; i < nThreads; i ++) {
  25. boolean success = false;
  26. try {
  27. // 创建 new NioEventLoop
  28. children[i] = newChild(executor, args);
  29. success = true;
  30. } catch (Exception e) {
  31. // TODO: Think about if this is a good exception type
  32. throw new IllegalStateException("failed to create a child event loop", e);
  33. } finally {
  34. if (!success) {
  35. for (int j = 0; j < i; j ++) {
  36. children[j].shutdownGracefully();
  37. }
  38.  
  39. for (int j = 0; j < i; j ++) {
  40. EventExecutor e = children[j];
  41. try {
  42. while (!e.isTerminated()) {
  43. e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
  44. }
  45. } catch (InterruptedException interrupted) {
  46. // Let the caller handle the interruption.
  47. Thread.currentThread().interrupt();
  48. break;
  49. }
  50. }
  51. }
  52. }
  53. }

继续跟踪进入newChild(executor, args)内部,看到它会返回一个NioEventLoop对象

  1. @Override
  2. protected EventLoop newChild(Executor executor, Object... args) throws Exception {
  3. return new NioEventLoop(this, executor, (SelectorProvider) args[0],
  4. ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
  5. }

继续查看NioEventLoop构造函数和他的父类构造函数

  1. NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
  2. SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
  3. super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
  4. if (selectorProvider == null) {
  5. throw new NullPointerException("selectorProvider");
  6. }
  7. if (strategy == null) {
  8. throw new NullPointerException("selectStrategy");
  9. }
  10. provider = selectorProvider;
  11. final SelectorTuple selectorTuple = openSelector();
  12. selector = selectorTuple.selector;
  13. unwrappedSelector = selectorTuple.unwrappedSelector;
  14. selectStrategy = strategy;
  15. }

父类构造函数

  1. /**
  2. * Create a new instance
  3. *
  4. * @param parent the {@link EventExecutorGroup} which is the parent of this instance and belongs to it
  5. * @param executor the {@link Executor} which will be used for executing
  6. * @param addTaskWakesUp {@code true} if and only if invocation of {@link #addTask(Runnable)} will wake up the
  7. * executor thread
  8. * @param maxPendingTasks the maximum number of pending tasks before new tasks will be rejected.
  9. * @param rejectedHandler the {@link RejectedExecutionHandler} to use.
  10. */
  11. protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
  12. boolean addTaskWakesUp, int maxPendingTasks,
  13. RejectedExecutionHandler rejectedHandler) {
  14. super(parent);
  15. this.addTaskWakesUp = addTaskWakesUp;
  16. this.maxPendingTasks = Math.max(16, maxPendingTasks);
  17. this.executor = ObjectUtil.checkNotNull(executor, "executor");
  18. taskQueue = newTaskQueue(this.maxPendingTasks);
  19. rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
  20. }

通过上面的代码我们可以看到,初始化NioEventLoop主要完成了以下的功能

1、保存线程执行器ThreadPerTaskExecutor

2、创建一个selector

3、基于LinkedBlockingQueue创建一个taskQueue任务队列,用于保存要执行的任务

这些都是为了后续的循环执行Channel 相关事件所做准备。

到这里其实我们创建了一组NioEventLoop,也就是一组事件循环执行器,每个NioEventLoop中都有对应的一个线程和一个selector ;创建完毕之后,自然就是要为每一个连接分配对应的NioEventLoop。Netty中通过

实现EventLoopGroup接口中的next()方法来返回一个可以使用的的NioEventLoop

  1. public interface EventLoopGroup extends EventExecutorGroup {
  2. /**
  3. * Return the next {@link EventLoop} to use
  4. */
  5. @Override
  6. EventLoop next();
  7. }

在MultithreadEventExecutorGroup中我们可以查看它的具体实现方式

  1. chooser = chooserFactory.newChooser(children);
  2.  
  3. @Override
  4. public EventExecutor next() {
  5. return chooser.next();
  6. }

进入代码内部我们可以看到Netty针对数组大小,对数组下标的计算方式进行了优化

  1. /**
  2. * Default implementation which uses simple round-robin to choose next {@link EventExecutor}.
  3. */
  4. @UnstableApi
  5. public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {
  6.  
  7. public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();
  8.  
  9. private DefaultEventExecutorChooserFactory() { }
  10.  
  11. @SuppressWarnings("unchecked")
  12. @Override
  13. public EventExecutorChooser newChooser(EventExecutor[] executors) {
  14. //判断是否是二的次幂,如果为true返回PowerOfTwoEventExecutorChooser,反之GenericEventExecutorChooser
  15. if (isPowerOfTwo(executors.length)) {
  16. return new PowerOfTwoEventExecutorChooser(executors);
  17. } else {
  18. return new GenericEventExecutorChooser(executors);
  19. }
  20. }
  21.  
  22. private static boolean isPowerOfTwo(int val) {
  23. return (val & -val) == val;
  24. }
  25.  
  26. private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
  27. private final AtomicInteger idx = new AtomicInteger();
  28. private final EventExecutor[] executors;
  29.  
  30. PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
  31. this.executors = executors;
  32. }
  33.  
  34. //通过&运算的方式循环获取数组下标
  35. @Override
  36. public EventExecutor next() {
  37. return executors[idx.getAndIncrement() & executors.length - 1];
  38. }
  39. }
  40.  
  41. private static final class GenericEventExecutorChooser implements EventExecutorChooser {
  42. private final AtomicInteger idx = new AtomicInteger();
  43. private final EventExecutor[] executors;
  44.  
  45. GenericEventExecutorChooser(EventExecutor[] executors) {
  46. this.executors = executors;
  47. }
  48.  
  49. //通过取模的方式循环获取数组下标
  50. @Override
  51. public EventExecutor next() {
  52. return executors[Math.abs(idx.getAndIncrement() % executors.length)];
  53. }
  54. }
  55. }

到此我们基本把Netty中NioEventLoop及NioEventLoopGroup的创建流程及核心代码梳理了一遍。NioEventLoop做为Netty线程模型的核心部分包含的内容比较多,上面只是初始化及创建的一部分内容,后续的部分我会陆续的补齐,其中有错误和不足之处还请指正与海涵。

关注微信公众号,查看更多技术文章。

Netty源码分析之NioEventLoop(一)—NioEventLoop的创建的更多相关文章

  1. Netty源码分析第2章(NioEventLoop)---->第1节: NioEventLoopGroup之创建线程执行器

    Netty源码分析第二章: NioEventLoop 概述: 通过上一章的学习, 我们了解了Server启动的大致流程, 有很多组件与模块并没有细讲, 从这个章开始, 我们开始详细剖析netty的各个 ...

  2. Netty源码分析第2章(NioEventLoop)---->第2节: NioEventLoopGroup之NioEventLoop的创建

    Netty源码分析第二章: NioEventLoop   第二节: NioEventLoopGroup之NioEventLoop的创建 回到上一小节的MultithreadEventExecutorG ...

  3. Netty源码分析第2章(NioEventLoop)---->第3节: 初始化线程选择器

    Netty源码分析第二章:NioEventLoop   第三节:初始化线程选择器 回到上一小节的MultithreadEventExecutorGroup类的构造方法: protected Multi ...

  4. Netty源码分析第2章(NioEventLoop)---->第4节: NioEventLoop线程的启动

    Netty源码分析第二章: NioEventLoop   第四节: NioEventLoop线程的启动 之前的小节我们学习了NioEventLoop的创建以及线程分配器的初始化, 那么NioEvent ...

  5. Netty源码分析第2章(NioEventLoop)---->第5节: 优化selector

    Netty源码分析第二章: NioEventLoop   第五节: 优化selector 在剖析selector轮询之前, 我们先讲解一下selector的创建过程 回顾之前的小节, 在创建NioEv ...

  6. Netty源码分析第2章(NioEventLoop)---->第6节: 执行select操作

    Netty源码分析第二章: NioEventLoop   第六节: 执行select操作 分析完了selector的创建和优化的过程, 这一小节分析select相关操作 跟到跟到select操作的入口 ...

  7. Netty源码分析第2章(NioEventLoop)---->第7节: 处理IO事件

    Netty源码分析第二章: NioEventLoop   第七节:处理IO事件 上一小节我们了解了执行select()操作的相关逻辑, 这一小节我们继续学习select()之后, 轮询到io事件的相关 ...

  8. Netty源码分析第2章(NioEventLoop)---->第8节: 执行任务队列

      Netty源码分析第二章: NioEventLoop   第八节: 执行任务队列 继续回到NioEventLoop的run()方法: protected void run() { for (;;) ...

  9. Netty源码分析之NioEventLoop(三)—NioEventLoop的执行

    前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...

  10. Netty 源码解析(七): NioEventLoop 工作流程

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第七篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...

随机推荐

  1. hibernate一级缓存和二级缓存的区别

    http://blog.csdn.net/defonds/article/details/2308972 缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了 ...

  2. React Js 之JSX

    React使用JSX作为模板替换JavaScript,它不是必须的,但是它是推荐使用.原因如下: 1.它比传统的JavaScript更快,因为编译代码的时候,JSX做了相应的优化 2.它是类型安全的, ...

  3. HSSFWorkBooK用法 —Excel表的导出和设置

    HSSFWorkBooK用法 —Excel表的导出和设置 2013年02月21日 ⁄ 综合 ⁄ 共 9248字 ⁄ 字号 小 中 大 ⁄ 评论关闭 public ActionResult excelP ...

  4. 关系数据库ORMlite的用法;

    首先要在 http://ormlite.com/releases 页面下载两个jar 包: (1)core列表下的jar包: (2)android列表下的jar包. 将上面的两个jar包下载后放到An ...

  5. Mysql Innodb 表碎片整理

    一.为什么会产生碎片 简单的说,删除数据必然会在数据文件中造成不连续的空白空间,而当插入数据时,这些空白空间则会被利用起来.于是造成了数据的存储位置不连续,以及物理存储顺序与理论上的排序顺序不同,这种 ...

  6. psycopg2 (python与postgresql)

    #快速导入数据到postgresql import pandas as pd import psycopg2 from io import StringIO def sql_to_df(): con= ...

  7. (转载)SAPI 包含sphelper.h编译错误解决方案

    [转]SAPI 包含sphelper.h编译错误解决方案 在使用Microsoft Speech SDK 5.1开发语音识别程序时,包含了头文件“sphelper.h”和库文件“sapi.lib”.编 ...

  8. C++ error C2440: “类型转换” : 无法从“std::vector::iterator”转换为“

    原文地址:http://blog.csdn.net/onlyou930/article/details/5602654 圆环套圆环之迭代器 话说这一日是风平浪静,万里乌云,俺的心情好的没得说,收到命令 ...

  9. 迁移HTML5移动项目到PhoneGap

    MyEclipse开年钜惠 在线购买低至75折!立即开抢>> [MyEclipse最新版下载] 一.创建一个新的PhoneGap应用程序项目 PhoneGap应用程序项目的结构与HTML5 ...

  10. Translate Exercises(3) 科技英语翻译

    [1] CMOS logic is a newer technology, based on the use of complementary MOS transistorsto perform lo ...