netty学习笔记整理一基础篇
一:.netty组件:
服务端启动过程:通过jdk创建channel并包装为自己的channel -> init初始化channel->注册selector ->dobind()往jdk注册一个op-accept实现就可以开始接受消息了
1.
NioEventLoopGroup
相当于serversocket接受连接bossgroup,传输消息的功能workgroup,
其实就是线程池管理
NioEventLoop
2.
NioEventLoop
真正接受链接,处理数据的组件,netty通过select来完成socket的接受和处理
3.channel
相当于socket,通过
.channel
(
NioServerSocketChannel.
class
)反射设置channel类型
channel过程:
jdk底层channel创建 -> channel TCP相关配置 -> 设置非阻塞模式 ->把channel注册到pipline中执行
4.handle
进行数据业务处理的组件
5.selector
channel 绑定eventloop 注册到jdk的selector,然后
回调注册的
ChannelInboundHandlerAdapter子类的
channelRead等
方法
二.NioEventLoop详解
问题引入:
1.netty服务端启动多少个线程(默认2*cpu),什么时候启动
2.jdk空转轮询的bug(主要是selector本来是阻塞的,但是当连接异常了,selector被唤醒了):通过时间判断selctor次数,超过512创建新的selector,把旧的key注册到新的selector
3.netty怎么实现异步串行无锁化的:handle是线程安全的,会把线程执行封装成task放到taskQueue中顺序执行
1.nioeventloop创建过程(构造创建selector和定时任务队列):
默认先创建2*cpu个线程(线程前缀:nioeventloop-'i'- j):'i 第几个group组'- j(第几个线程)
每一个线程就是一个EventExecutor,也就是
nioeventloop。然后创建EventExecutorChooser ,这个东西就是为了当有链接进来的时候进行loop线程绑定,这边有做性能优化,如果线程数是2的幂等就做位运算来提高绑定效率
public
EventExecutorChooser newChooser
(
EventExecutor
[]
executors
) {
if
(
isPowerOfTwo
(
executors.
length
)) {
return new
PowerOfTwoEventExecutorChooser
(
executors
)
;
}
else
{
return new
GenericEventExecutorChooser
(
executors
)
;
}
}
2.nioeventloop启动
1.绑定端口
2.主线程使用
nioeventloop创建过程
构造函数 创建的线程task->存在
taskQueue里面->
启动run方法
3.nioeventloop执行(run方法)
for
(
;;
) 判断selector类型即检测io事件 ->
select方法调用->
从
taskQueue中取任务进行执行
select方法:判断selctor是否被唤醒/taskQueue是否被其他线程添加任务/是否有定时任务 满足条件执行,否则阻塞1s
执行任务:
runAllTasks:会把定时任务和普通任务合并一起执行
(通过debug发现,一次selector事件可以执行多次任务,比如client没10s发一次消息,如果debug阻塞10次,然后10次会循环输出)
然后client发送消息会继续走nioEventLoop.run() ->
runAllTasks()方法 就是有两个for循环
client执行任务调用的是:
runAllTasks()方法, server接受读写任务是:
processSelectedKeys
() 方法
三.netty 连接处理
1.新连接接入控制速率一次16个
2.新连接处理的channel:niosocketChannel
3.niosocketChannel 客户端channel构造函数注册的是op_read事件,nioserversocketchannel 服务端channel注册的是accept事件
4.新连接NioEventLoop的分配和selector注册
添加childHandler->设置options和attrs(主要设置用户设置的一些tcp参数)->选择nioeventloop并注册selector
新链接处理逻辑总结:服务端检测到新连接 ->创建nioSocketChannel->绑定nioeventloop,注册selector,注册读事件
四.
pipeline和channelHandle
inBound事件的传播:
添加channelHandle:封装为channelHandleContext 放入到pipline链表中,然后触发回调通知用户已经添加
inBound是从pipline链表从头到尾执行,如果执行到最后节点,tailContext :处理未捕获的异常/未处理的消息会debug输出建议你去处理并释放消息
outBound
事件的传播: wirte()进行回写,给client响应
outBound是从pipline链表从尾到头执行,如果中间节点不传播继续write()则结束
异常的传播:
如果当前handleA报错,会把错误继续向下个节点顺序传播,直到被处理,通过复写exceptionCaught处理异常
异常处理最佳实践:hadle链增加一个exceptionhandle。处理