原文博客:Doi技术团队
链接地址:https://blog.doiduoyi.com
初心:记录优秀的Doi技术团队学习经历
第一章节是主要是服务器启动的代码分析。章节目录有:
|---------1.1初始化NioEventLoopGroup
|---------1.2初始化NioEventLoop
|---------1.3初始化NioServerSocketChannel
|---------1.4服务器启动流程
为什么先从初始化开始了解服务器启动?
因为在我看服务器启动的相关源码的时候,有很多地方都是初始化的时候已经建立好的。所以我就从初始化的源码开始看起。这是我第一次看源码的笔记,仍有很多理解错误的地方和不解的地方。欢迎讨论。
本篇目录:
- 继承类关系图
- 初始化流程图
- 代码分析
- 疑问
继承类关系图
初始化流程图
1 NioEventLoop
- 1 初始化父类
SingleThreadEventLoop
- 分析一下NioEventLoop构造的参数
第一个参数NioEventLoopGroup parent
:该NioEventLoop所在的线程池
第二个参数Executor executor
:ThreadPerTaskExecutor(newDefaultThreadFactory())
的一个执行器。
第三个参数SelectorProvider
:主要是provider.openSelector()打开Selector。
第四个参数SelectStrategy
:一个选择策略,当线程进行事件循环的时候,就是根据该类来进行判断。
第五个参数RejectedExecutionHandler
:一个处理异常的类
此时父类已经初始化完成,接下来是NioEventLoop类的一个初始化。
- 2 获得一个
SelectorTuple
对象,该对象其实装着Selector。这一步就是通过selectorProvider获得Selector的
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
//1
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;
final SelectorTuple selectorTuple = openSelector();//2
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
SingleThreadEventLoop
NioEventLoop 父类的SingleThreadEventLoop
有参构造
有两个新参数
第一个int addTaskWakesUp
: 默认值 false
第二个int maxPendingTasks
: 默认值DEFAULT_MAX_PENDING_TASKS
- 1 对父类进行初始化
- 2 建立一个LinkedBlockingQueue的任务队列
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedExecutionHandler) {
//1
super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
tailTasks = newTaskQueue(maxPendingTasks); //2
}
SingleThreadEventExecutor
SingleThreadEventLoop 父类的SingleThreadEventLoop
有参构造
- 1 是最终对父类
AbstractEventExecutor
的一个变量EventExecutorGroup 复制。 - 2 赋值给addTaskWakesUp ,当该线程有添加任务的时候就会改为ture,用来唤醒线程
- 3 赋值给maxPendingTasks,该线程最大允许任务数
- 4 建立一个任务队列
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedHandler) {
super(parent);//1
this.addTaskWakesUp = addTaskWakesUp;//2
this.maxPendingTasks = Math.max(16, maxPendingTasks); //3
this.executor = ObjectUtil.checkNotNull(executor, "executor");
taskQueue = newTaskQueue(this.maxPendingTasks); //4
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
总结
NioEventLoop
类实现的是单线程处理多个channel或者channel。那么它就应该是具有channel的一些信息以及与一条thread线程绑定在一起。如图:
疑问
- 在
SingleThreadEventLoop
创建了一个tailTasks任务队列,在SingleThreadEventExecutor
创建了一个taskQueue任务队列。他们分别有什么用?
我也不太懂,应该是扩展设计吧。 - 在
SingleThreadEventExecutor
有两个 Executor 和Thread 变量。分别代表什么意思?
在4.0.x 版本,只有Thread 这个变量。在初始化的时候,SingleThreadEventExecutor
有参构造会创建一个thread线程。并将这个线程赋予给Thread变量。
而在4.1.x 版本。就有 Executor 和Thread 变量两个变量。在初始化的时候,是赋予 Executor 变量一个ThreadPertTaskExcutor对象。此时并没有用该Executor创建一个线程,thread也是空的。 直到调用SingleThreadEventExecutor
的execute()方法 才会生成一个线程并赋值与Thread变量