Netty 中 EventLoopGroup 的创建
本文是基于 Netty 4.1.6.Final 的源码来分析的。
在分析源码之前做一些准备工作:
先熟悉一下 IDEA 的几个快捷键,能极大的提高我们查看源码的效率:
- Ctrl + Alt + B:用鼠标点击指定的方法,然后按下快捷键,IDEA 就会跳转到该方法的定义的地方,如果是重写的方法,则会列出该方法的所有实现;
- Ctrl + Alt + ←/→:跳转至前/后一次鼠标点击的地方,方便我们来回查看源码;
- Ctrl + F12:弹出当前类的所有方法,可以直接敲字母来过滤方法;
- Shift + F7:Debug 的时候,当一行代码中链式的调用了多个方法,按下该快捷键会弹出改行所有的方法,然后选择要进入的方法,查看源码。
1. 创建过程
- 创建 1 个 executor,后续用来创建并执行线程;
- 创建指定数量的 EventLoop;
- 为当前 EventLoop 创建 1 个 selector;
- 根据 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 的创建的更多相关文章
- 聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇)
本系列Netty源码解析文章基于 4.1.56.Final版本 在上篇文章<聊聊Netty那些事儿之从内核角度看IO模型>中我们花了大量的篇幅来从内核角度详细讲述了五种IO模型的演进过程以 ...
- Netty中NioEventLoopGroup的创建源码分析
NioEventLoopGroup的无参构造: public NioEventLoopGroup() { this(0); } 调用了单参的构造: public NioEventLoopGroup(i ...
- Netty中的ChannelFuture和ChannelPromise
在Netty使用ChannelFuture和ChannelPromise进行异步操作的处理 这是官方给出的ChannelFutur描述 * | Completed successfully | * + ...
- Netty中的ChannelPipeline源码分析
ChannelPipeline在Netty中是用来处理请求的责任链,默认实现是DefaultChannelPipeline,其构造方法如下: private final Channel channel ...
- netty中的EventLoop和EventLoopGroup
Netty框架的主要线程就是I/O线程,线程模型设计的好坏,决定了系统的吞吐量.并发性和安全性等架构质量属性. 一.Netty的线程模型 在讨论Netty线程模型时候,一般首先会想到的是经典的Reac ...
- Netty学习之客户端创建
一.客户端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty客户端开发步骤 使用Netty进行客户端开发主要有以下几个步骤: 1.用户线程创建Bootstrap Bootstrap b ...
- Netty学习之服务器端创建
一.服务器端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty服务器端开发步骤 使用Netty进行服务器端开发主要有以下几个步骤: 1.创建ServerBootstrap实例 Serv ...
- 序列化在Netty中的使用
Java序列化的缺点 1.无法跨语言 对于Java序列化后的字节数组,别的语言无法进行反序列化 2.序列化后的码流过大 3.序列化性能低 使用JDK自带的序列化进行对象的传输 被传输的,实现了序列化接 ...
- Netty中的基本组件及关系
原文:https://blog.csdn.net/summerZBH123/article/details/79344226--------------------- 概述 这篇文章主要是用来 ...
随机推荐
- QVector常见使用方法
仅在此简单介绍QVector的一些常见函数,有兴趣的可以查下QT,在QT中介绍的很详细 构造函数,QVector的构造函数很多样化,常见的有 QVector() 无参的构造函数 QVector(int ...
- 随手记-egg入门
egg 入门 https://eggjs.org/zh-cn/intro/quickstart.html 1.建立项目目录2. npm i egg --save && npm i ...
- 微信浏览器无法下载APK文件的解决方案
大家是不是经常会遇到微信内点击链接或扫描二维码无法打开指定网页的问题?只要你使用微信转发分享,相信你就一定会遇到,那么打不开的原因很简单了,就是被微信拦截了.这个问题我们只需要实现从微信内直接跳出到外 ...
- 生产环境ssh登陆策略
生产环境ssh登陆策略 备份:cp /etc/ssh/sshd_config{,.bak} vim /etc/ssh/sshd_config 17 #Port 22 #修改ssh连接端口 38 # ...
- jquery的js代码兼容全部浏览器的解决方法
//以下均可console.log()实验 var winW=document.body.clientWidth||document.docuemntElement.clientWidth;//网 ...
- linux安装anaconda3 conda: command not found
在使用Anaconda3时出现: conda:未找到命令 最后发现每次开机后都要运行一个命令才行:export PATH=~/anaconda3/bin:$PATH 如果要永久保存路径: 1.在终端输 ...
- 如何理解JavaScript中的原型和原型链
首先是一张关系图,避免抽象化理解时产生的困难 Function对象 函数对象是JavaScript学习中不可避免的一部分,而且这一部分相对重要且抽象 函数的创建方式有2种: 字面量创建 var foo ...
- Python中类的__init__继承
Python中类的__init__继承 概念: 定义父类 In [10]: class Person: ....: def __init__(self,name,age,sex): ....: sel ...
- 深度解析synchronized的实现原理(并发一)
一.synchronized实现原理 1.synchronized实现同步的基础: 1).普通同步方法:锁是当前实例对象 2).静态同步方法:锁是当前类的class对象 3).同步方法块:锁是括号里面 ...
- 关于js数组的简单复制
var a=[]; a.push(1); a.push(2); a.push(3); var b=a; b[0]=4; alert(a);//4,2,3 alert(b);//4,2,3 这种写法由于 ...