参考闪电侠https://www.jianshu.com/u/4fdc8c2315e8系列文章
服务端启动总结:
1.设置启动类参数,最重要的就是设置channel
2.创建server对应的channel,创建各大组件,包括ChannelConfig,ChannelId,ChannelPipeline,ChannelHandler,Unsafe等
3.初始化server对应的channel,设置一些attr,option,以及设置子channel的attr,option,给server的channel添加新channel接入器,并出发addHandler,register等事件
4.调用到jdk底层做端口绑定,并触发active事件,active触发的时候,真正做服务端口绑定
新连接接入:
1.boos reactor线程轮询到有新的连接进入
2.通过封装jdk底层的channel创建 NioSocketChannel以及一系列的netty核心组件
3.将该条连接通过chooser,选择一条worker reactor线程绑定上去
4.注册读事件,开始新连接的读写
reactor线程干的事儿:
https://img.haomeiwen.com/i1357217/67ed6d1e8070426f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000/format/webp
1.轮询IO事件
2.处理轮询到的事件
3.执行任务队列中的任务
轮询IO事件:
不断地轮询是否有IO事件发生,并且在轮询的过程中不断检查是否有定时任务和普通任务,保证了netty的任务队列中的任务得到有效执行,轮询过程顺带用一个计数器避开了了jdk空轮询的bug,过程清晰明了
处理轮询到的事件:
netty的reactor线程第二步做的事情为处理IO事件,netty使用数组替换掉jdk原生的HashSet来保证IO事件的高效处理,每个SelectionKey上绑定了netty类AbstractChannel对象作为attachment,在处理每个SelectionKey的时候,就可以找到AbstractChannel,然后通过pipeline的方式将处理串行到ChannelHandler,回调到用户方法。
执行任务队列中的任务:
当前reactor线程调用当前eventLoop执行任务,直接执行,否则,添加到任务队列稍后执行
netty内部的任务分为普通任务和定时任务,分别落地到MpscQueue和PriorityQueue
netty每次执行任务循环之前,会将已经到期的定时任务从PriorityQueue转移到MpscQueue
netty每隔64个任务检查一下是否该退出任务循环
pipline 总结:
1.以新连接创建为例,新连接创建的过程中创建channel,而在创建channel的过程中创建了该channel对应的pipeline,创建完pipeline之后,自动给该pipeline添加了两个节点,即ChannelHandlerContext,ChannelHandlerContext中有用pipeline和channel所有的上下文信息。
2.pipeline是双向个链表结构,添加和删除节点均只需要调整链表结构
3.pipeline中的每个节点包着具体的处理器ChannelHandler
,节点根据ChannelHandler
的类型是ChannelInboundHandler
还是ChannelOutboundHandler
来判断该节点属于in还是out或者两者都是
4.一个Channel对应一个Unsafe,Unsafe处理底层操作,NioServerSocketChannel对应 NioMessageUnsafe, NioSocketChannel对应NioByteUnsafe
5.inBound事件从head节点传播到tail节点,outBound事件从tail节点传播到head节点
6.异常传播只会往后传播,而且不分inbound还是outbound节点,不像outBound事件一样会往前传播
writeAndFlush:
1.pipeline中的编码器原理是创建一个ByteBuf,将java对象转换为ByteBuf,然后再把ByteBuf继续向前传递
2.调用write方法并没有将数据写到Socket缓冲区中,而是写到了一个单向链表的数据结构中,flush才是真正的写出
3.writeAndFlush等价于先将数据写到netty的缓冲区,再将netty缓冲区中的数据写到Socket缓冲区中,写的过程与并发编程类似,用自旋锁保证写成功
4.netty中的缓冲区中的ByteBuf为DirectByteBuf
网友评论