本文是基于 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. OSS文件上传到阿里云

    <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-4.4.4.min.js"></script&g ...

  2. react-redux中的数据传递

    1.connect connect用于连接React组件与 Redux store,其使用方法如下 connect([mapStateToProps], [mapDispatchToProps], [ ...

  3. 答题卡作文模块的一种方法-VSTO

    在开始做之前,首先百度了Word有没有简单的生成方法,果然有--页面布局->稿纸设置->方格式稿纸 效果如下图所示.很规范,但是不是答题卡所需要的,因为这样会把所有页面都设置为这样的稿纸. ...

  4. 安装vue-cli时-4058报错的解决方法

    一.报错信息 安装vue-cli时-4058报错 二.解决办法 1.安装淘宝镜像 npm --registry https://registry.npm.taobao.org info undersc ...

  5. 小程序数据绑定点赞效果切换(交流QQ群:604788754)

    如果对本例有更好的意见和建议,希望给予留言或是加群跟群主联系,交流学习. WXML: <block wx:for="{{nums}}" wx:for-index='idx' ...

  6. EChart.js 简单入门

    EChart.js 简单入门 最近有一个统计的项目要做,在前端的数据需要用图表的形式展示.网上搜索了一下,发现有几种统计图库. MSChart   这个是Visual Studio里的自带控件,使用比 ...

  7. 保存cookie状态封装

    from urllib import request, parsefrom urllib.error import HTTPError,URLError#保存cookiefrom http impor ...

  8. elment重置表格行高,hover效果

    来源网络,做个笔记.表头行高.el-table__header tr, .el-table__header th { padding: 0; height: 50px; }表体行高 .el-table ...

  9. PHP运算符知识

    1.三目运算符: $a =1; echo $a>0 ? '大于0':$a==0 ? '等于0':'小于0'; 貌似应该输出:大于0 其实: 然而,上面语句的实际输出是't',因为三元运算符是从左 ...

  10. JavaScript中事件委托(事件代理)详解

    在JavaScript的事件中,存在事件委托(事件代理),那么什么是事件委托呢? 事件委托在生活中的例子: 有三个同事预计会在周一收到快递.为签收快递,有两种办法:一是三个人在公司门口等快递:二是委托 ...