本文是基于 Netty 4.1.6.Final 的源码来分析的。

  在分析源码之前做一些准备工作:

  先熟悉一下 IDEA 的几个快捷键,能极大的提高我们查看源码的效率:

  1. Ctrl + Alt + B:用鼠标点击指定的方法,然后按下快捷键,IDEA 就会跳转到该方法的定义的地方,如果是重写的方法,则会列出该方法的所有实现;
  2. Ctrl + Alt + ←/→:跳转至前/后一次鼠标点击的地方,方便我们来回查看源码;
  3. Ctrl + F12:弹出当前类的所有方法,可以直接敲字母来过滤方法;
  4. Shift + F7:Debug 的时候,当一行代码中链式的调用了多个方法,按下该快捷键会弹出改行所有的方法,然后选择要进入的方法,查看源码。

1. 创建过程

  1. 创建 1 个 executor,后续用来创建并执行线程;
  2. 创建指定数量的 EventLoop;
    1. 为当前 EventLoop 创建 1 个 selector;
  3. 根据 EventLoop 的数量创建指定类型的 chooser,后续用来分配线程。

2. 代码

  EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

  这两行代码创建的 EventLoopGroup 分别用来处理新连接的接入和已接入连接的事件处理。

3. 源码分析

   NioEventLoopGroup 的构造方法,最终调用的是 MultithreadEventExecutorGroup 的构造方法:

 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
//...
//1. 创建 executor
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// EventLoopGroup 中的 EventLoop 数组
children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//2. 创建 EventLoop
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);
} finally {
//...
}
}
//3. 创建 chooser
chooser = chooserFactory.newChooser(children);
//..
}

3.1 executor 的创建

  创建 executor 的构造方法中传入了 1 个 DefaultThreadFactory:

 public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
//... poolName 的值是 EventLoopGroup 的类名,首字母小写
prefix = poolName + '-' + poolId.incrementAndGet() + '-';
this.daemon = daemon;
this.priority = priority;
this.threadGroup = threadGroup;
}

  ThreadPerTaskExecutor  类:

 public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory; public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
} @Override
public void execute(Runnable command) {
//通过线程工厂创建并启动线程
threadFactory.newThread(command).start();
}
}

  DefaultThreadFactory 的 newThread(Runnable command)方法:

 @Override
public Thread newThread(Runnable r) {
//调用了后面的方法,最终创建的是 Netty 封装的 FastThreadLocalThread
Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());
try {//线程相关的设置
if (t.isDaemon()) {
if (!daemon) {
t.setDaemon(false);
}
} else {
if (daemon) {
t.setDaemon(true);
}
} if (t.getPriority() != priority) {
t.setPriority(priority);
}
} catch (Exception ignored) {
// Doesn't matter even if failed to set.
}
return t;
}
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(threadGroup, r, name);
}

  注意:这里只是分析了 executor 的创建,以及它创建线程的方法,这一阶段并没有创建和运行新线程。

3.2 EventLoop 的创建

  newChild()方法将 executor 传了进去,这里以 NioEventLoop 举例,所以最终调用了 NioEventLoop 的构造方法:

 NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
//executor 最终传递给父类 SingleThreadEventExecutor
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
//创建 selector
selector = openSelector();
selectStrategy = strategy;
}

  在 NioEventLoop 几个父类的构造方法中,创建了任务队列,暂时不做分析。

3.3 chooser 的创建

  chooser 也是通过工厂模式创建的,参数 children 是前面创建的 EventLoop 数组,chooserFactory 会根据数组的长度是否为 2 的幂来创建 chooser。

 @SuppressWarnings("unchecked")
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTowEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
} private static boolean isPowerOfTwo(int val) {
//判断数组长度是否为 2 的幂
//有符号数的计算:以 Byte 为例
// 3 ---> 0000 0011 0000 0011
// -3 ---> 1000 0000 - 0000 0011 = 0111 1101 ===> 1111 1101 &
// 0000 0000
// 2 ---> 0000 0010 0000 0010
// -2 ---> 1000 0000 - 0000 0010 = 0111 1110 ===> 1111 1110 &
// 0000 0010
return (val & -val) == val;
} private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors; PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
} @Override
public EventExecutor next() {
//因为 length 是 2 的幂,减去 1,退一位,二进制就全是 1
//比如 8 是 1000,减 1 是 0111,将 idx 自增后和前面的值相与
//相当于是循环取值
return executors[idx.getAndIncrement() & executors.length - 1];
}
} private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors; GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
} @Override
public EventExecutor next() {
//普通的就是直接取模的绝对值
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}

  至此,EventLoopGroup 就创建完成了,boosGroup 和 wrokerGroup 的创建是一样的。

Netty 中 EventLoopGroup 的创建的更多相关文章

  1. 聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇)

    本系列Netty源码解析文章基于 4.1.56.Final版本 在上篇文章<聊聊Netty那些事儿之从内核角度看IO模型>中我们花了大量的篇幅来从内核角度详细讲述了五种IO模型的演进过程以 ...

  2. Netty中NioEventLoopGroup的创建源码分析

    NioEventLoopGroup的无参构造: public NioEventLoopGroup() { this(0); } 调用了单参的构造: public NioEventLoopGroup(i ...

  3. Netty中的ChannelFuture和ChannelPromise

    在Netty使用ChannelFuture和ChannelPromise进行异步操作的处理 这是官方给出的ChannelFutur描述 * | Completed successfully | * + ...

  4. Netty中的ChannelPipeline源码分析

    ChannelPipeline在Netty中是用来处理请求的责任链,默认实现是DefaultChannelPipeline,其构造方法如下: private final Channel channel ...

  5. netty中的EventLoop和EventLoopGroup

    Netty框架的主要线程就是I/O线程,线程模型设计的好坏,决定了系统的吞吐量.并发性和安全性等架构质量属性. 一.Netty的线程模型 在讨论Netty线程模型时候,一般首先会想到的是经典的Reac ...

  6. Netty学习之客户端创建

    一.客户端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty客户端开发步骤 使用Netty进行客户端开发主要有以下几个步骤: 1.用户线程创建Bootstrap Bootstrap b ...

  7. Netty学习之服务器端创建

    一.服务器端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty服务器端开发步骤 使用Netty进行服务器端开发主要有以下几个步骤: 1.创建ServerBootstrap实例 Serv ...

  8. 序列化在Netty中的使用

    Java序列化的缺点 1.无法跨语言 对于Java序列化后的字节数组,别的语言无法进行反序列化 2.序列化后的码流过大 3.序列化性能低 使用JDK自带的序列化进行对象的传输 被传输的,实现了序列化接 ...

  9. Netty中的基本组件及关系

    原文:https://blog.csdn.net/summerZBH123/article/details/79344226---------------------  概述    这篇文章主要是用来 ...

随机推荐

  1. CentOS 7 配置nginx并默认强制使用https对http进行跳转

    1.安装nginx yum install nginx 2.启动nginx服务 service nginx start 3.开启防火墙80端口,云服务器和本地虚拟服务器各有不同,不再赘述. 4.访问你 ...

  2. Hive中数据的导入与导出

    最近在做一个小任务,将一个CDH平台中Hive的部分数据同步到另一个平台中.毕竟我也刚开始工作,在正式开始做之前,首先进行了一段时间的练习,下面的内容就是练习时写的文档中的内容.如果哪里有错误或者疏漏 ...

  3. Objective-C基础教程 笔记

    一.对C的扩展 1. #import VS #include C语言使用#include语句通知编译器应在头文件中查询定义. OC中也可以使用#include,但几乎不这么用,而是使用#import. ...

  4. bash: ./xxx 权限不够

    Linux环境下要运行C编译的一个可执行文件play,终端cd到当前目录后输入./play,提示  bash: ./xxx 权限不够 用sudo, 提示 sudo:./play: command no ...

  5. 牛客网第9场多校E(思维求期望)

    链接:https://www.nowcoder.com/acm/contest/147/E 来源:牛客网 题目描述 Niuniu likes to play OSU! We simplify the ...

  6. 前端必备之Node+mysql+ejs模版如何写接口

    前端必备之Node+mysql+ejs模版如何写接口 这星期公司要做一个视频的后台管理系统, 让我用Node+mysql+ejs配合写接口, 周末在家研究了一下, 趁还没来具体需求把研究内容在这里分享 ...

  7. spark优化整理

  8. js定义函数方式有。。。

    1,常见方式 function  fun(data,...){ console.log(data); } 注:随处可调用 2. 函数直接定义函数 var fun = function (data,.. ...

  9. 对羊车门的思考/python/

    作业完成人: 学号:20181603048,温晨阳 学号:20181603024,刘鑫垚 题目描述:有3扇关闭的门,一扇门后面停着汽车,其余门后是山羊,只有主持人知道每扇门后面是什么.参赛者可以选择一 ...

  10. SQL Server数据库开发的二十一条军规

    如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS).在这 ...