Netty源码分析(三):NioEventLoop启动

NioEventLoop的启动有两大触发器:
在这里插入图片描述
先来说服务端启动绑定端口
NioEventLoop的启动流程
在这里插入图片描述
首先,bind方法将具体绑定的操作封装成一个task,然后调用NioEventLoop的execute方法,判断调用execute的线程不是Nio
线程,于是调用startThread方法尝试创建线程,创建线程使用的是线程创建器ThreadPerTaskExecutor,创建完线程后NioEventLoop
会将此线程进行保存,保存的目的就是为了判断后续对NioEventLoop相关的执行线程是否是本身,如果不是,就封装成一个task,扔到
taskQueue中串行执行,能够保证线程安全。最后,调用NioEventLoop的run方法,run方法是驱动Netty运转的核心方法。
下面 通过走一遍源码来理解这个过程
在这里插入图片描述
追踪execute方法,实现类是SingleThreadEventExecutor
在这里插入图片描述
inEventLoop是一个布尔类型,用于判断当前调用execute方法的线程是否是与NioEventLoop唯一绑定的线程
点进inEventLoop方法看一下:
在这里插入图片描述
可以看到,传入的参数是当前线程,继续追踪SingleThreadEventExecutor的该方法
在这里插入图片描述
这里就可以看到只是做了比较运算。
因为第一次判断,NioEventLoop没有创建线程,所以这里是null,返回false
代码也就进入了这里:
在这里插入图片描述
追踪startThread方法
在这里插入图片描述
继续追踪doStartThread方法
在这里插入图片描述
重点关注this.executor.execute(…)
这里的executor就是线程创建器,在NioEventLoop的创建中,第一个步骤就是new一个线程创建器
翻出当时的源码:
在这里插入图片描述
调用了execute方法,方法的内容:
在这里插入图片描述
第一个划线是将当前线程绑定为NioEventLoop唯一的线程,也就是一步赋值操作
第二个划线是调用run方法,进行实际的启用,也就是NioEventLoop的执行过程。
这个run方法到底做了什么?
先来看看大体流程图
在这里插入图片描述
run方法是一个死循环,循环中主要做三件事情,首先调用select方法,轮询注册到selector上面的事件,然后调用
processSelectedKeys()方法处理轮询到的事件,最后调用runAllTasks方法来处理外部线程扔到taskQueue里的任务。
第一个过程:
在这里插入图片描述
通过调用select方法来检查是否有io事件
select方法的执行逻辑:
在这里插入图片描述
select操作也是一段无限for循环,首先它会计算一下本次执行select的截止时间,截止时间主要是根据NioEventLoop是否有定时任务
需要处理,以及判断在select的时候是否有任务需要处理,也就是说,在select的时候,如果需要执行一个任务,select就会停止,否则
进入上面一个过程,也就是说,如果没有到截止时间或者任务队列为空,那就会进行一次阻塞式的select操作,默认情况是1s,当然外部线程
也可以把select操作唤醒,最后一个过程是避免jdk空轮询的bug,Netty使用了一个巧妙的方式避免了这个bug。
deadline以及任务穿插逻辑处理:
先回到select方法的代码片段:
在这里插入图片描述
这里的wakenUp是一个状态,代表select是否被唤醒,也就是标识了这次select是未唤醒状态
点入select方法:
在这里插入图片描述
这里主要是对每次循环做了时间控制,如果本次select的时间超过了截止时间,那么就会跳出for循环,
timeoutMillis是指剩余时间,剩余时间<0,则说明超时。
跳出循环之前还会判断select轮询的次数,如果是0的话,还会进行一次非阻塞的select,然后再跳出循环。
跳出循环则意味着本次select结束。
继续往下看:
在这里插入图片描述
这块主要是判断任务队列是否为空,点入hasTasks看一下
在这里插入图片描述
所以这块代码的意思就是说如果任务队列为空,就会调用一次非阻塞的select方法
继续往下看:
在这里插入图片描述
如果时间没有到而且队列也不为空,这时就会执行一次阻塞的select,阻塞的事件为timeoutMillis,也就是剩余时间
完成select后,selectCnt++,表示select轮询了selectCnt次了。
在这里插入图片描述
这块表示五个条件满足一个就跳出循环,结束本次select操作
(1)select something,正在某个select中
(2)当前select操作是否需要唤醒
(3)在执行select时已经被外部线程唤醒
(4)任务队列里面有任务
(5)定时任务队列里面有任务
下面是空轮询的判断代码块
在这里插入图片描述
首先是对时间做了判断,如果当前事件减去剩余时间大于开始时间的话,说明已经进行了阻塞的select
上面说见了阻塞的select的阻塞时间长为剩余时间,所以置selectCnt=1,如果小于,则说明没有阻塞,在没有阻塞的
情况下并且轮询次数(selectCnt)大于512,说明是空轮询bug,那么就会进入else代码块,rebuildSelector()方法巧妙的处理
了这个bug
rebuildSelector():
在这里插入图片描述
首先会重新创建一个Selector
在这里插入图片描述
然后会遍历旧的Selector.keys,拿到SelectionKey和attachment,将SelectionKey重新注册到新的Selector
attachment是Netty封装后的一个Channel,如果AbstractNioChannel的实例,就修改selectionKey为新的SelectionKey
重新更换Selector后,会再次进行一次非阻塞的Select:
在这里插入图片描述

第二,三个过程:

在这里插入图片描述
processSelectedKeys()方法是用来处理轮询到的IO事件
runAllTasks()用来处理异步任务队列
这里可以发现下面else语句中也调用了这个方法,这里出现分支是为了控制这两个方法执行的时间
先来看processSelectedKeys()处理io事件的流程:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值