本文是基于 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. dva.js 上手

    来源:https://pengtikui.cn/dva.js-get-started/ ——------------------------------------------------------ ...

  2. list map to map

    Map<String, Object> merged = lists.stream() .map(Map::entrySet) .flatMap(Set::stream) .collect ...

  3. 共识机制:AngelToken技术的根基

    共识机制是区块链技术的一个核心问题,它决定了区块链中区块的生成法则,保证了各节点的诚实性.账本的容错性和系统的稳健性. 常用的共识机制主要有 PoW.PoS.DPoS.Paxos.PBFT等. 基于区 ...

  4. balcanced-binary-tree

    题目描述 Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced bi ...

  5. Vue混入

    Vue 混入 混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项. 数据对象合 ...

  6. docker网络

    docker网络   Docker 允许通过外部访问容器或容器互联的方式来提供网络服务. 端口映射允许外部访问容器 --link 容器互联 容器桥接网络 .通过--link容器通信,给test2添加一 ...

  7. python中文件读写

    读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘, ...

  8. 正则表达式判断QQ号格式是否正确

    #正在表达式匹配QQ号格式是否正确#QQ号假如长度为5-11位,纯为数字 import rewhile 1: qq=input("请输入QQ号:") result=re.finda ...

  9. jmeter如何进行MQTT性能测试(测试前期准备一,性能测试需求)

    接到一个有关MQTT的性能测试任务,把查找资料到解决问题的过程都记录.分享下 首先先科普下性能测试中相关术语的解释及 说到性能测试.负载测试.压力测试.并发测试,很多人都是混合使用,或者一会叫压力测试 ...

  10. 获取百度地图POI数据二(准备搜索关键词)

    上篇讲到  想要获取尽可能多的POI数据 需要准备尽可能多的搜索关键字   那么这些关键字如何得来呢?   本人使用的方法是通过一些网站来获取这些关键词   http://poi.mapbar.com ...