Netty(Reactor线程模型/零拷贝/空轮询)
2021-02-26 09:24:53 3 举报
AI智能生成
Netty(Reactor线程模型/零拷贝/空轮询)
作者其他创作
大纲/内容
喜欢收藏+点赞👍 谢谢
Reactor线程模型
单线程模型
事件的注册、监听、处理始终只有一个线程去完成
缺点:如果处理过程太久则整个过程都会阻塞
多线程模型
事件的注册监听由一个线程完成,事件的处理交给线程池
缺点:高并发场景下还是会有不小的延迟
主从架构
事件的注册由主线程完成,只处理连接事件;监听事件和处理事件由线程池完成,只处理读事件
根据CPU个数创建多个Selector选择器
Netty的线程模型
这个要看我们如何编码,NioEventLoopGroup默认线程数为cpu核心数的两倍
如果使用了两个NioEventLoopGroup
且指定工作线程数不为一,则是主从多线程模型
且指定工作线程数为一,则是主从单线程模型
如果使用了一个NioEventLoopGroup
且指定工作线程数不为一,则是多线程模型
且指定工作线程数为一,则是单线程模型
零拷贝
点击跳转 👉
扫码关注公众号,回复“零拷贝”领取密码
TCP 粘包拆包问题
什么是粘包拆包?
操作系统在发送TCP数据的时候,底层会有一个缓冲区,例如1024个字节大小,如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题;如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包,也就是将一个大的包拆分为多个小包进行发送
解决
常见解决方案
客户端在发送数据包的时候,每个包都固定长度,比如1024个字节大小,如果客户端发送的数据长度不足1024个字节,则通过补充空格补全到指定长度
客户端在每个包的末尾使用固定的分隔符,例如\\,如果一个包被拆分了,则等待下一个包发送过来之后找到其中的\\,将两部分合并可得一个完整包
将消息分为头部和消息体,在头部中保存有当前整个消息的长度,只有在读取到足够长度的消息之后才算是读到了一个完整的消息
通过 自定义协议 进行粘包和拆包的处理
Netty解决方案:内置了4个解码器
1、FixedLengthFrameDecoder:基于固定长度的解码器(应用场景:使用固定长度处理粘包和拆包)
3、DelimiterBasedFrameDecoder:基于分隔符的解码器
4、LengthFieldBasedFrameDecoder:指定长度的解码器(应用场景:配合LengthFieldPrepender编码器使用)
应用场景:使用分隔符处理粘包拆包前者使用\\,后者要自定义分隔符
空轮询bug
发生
selector.select() 本应该在有就绪事件到达前一直阻塞,但很遗憾,由于 Java NIO 实现上存在 bug,select() 可能在没有任何就绪事件的情况下返回,从而导致 while(true) 被不断执行,导致CPU占用100%
检测 epoll bug
selector.select(timeoutMillis) 阻塞时间小于 timeoutMillis,且
select 执行次数 > 阈值(默认 512)
重建Selector
新建一个 Selector
span style=\
关闭旧 Selector
代码实现逻辑(Netty)
常见问题
Channel 与 Socket 是什么关系?
Socket:网络上两个程序通过一个双向的通信连接实现数据交换,这个连接的端点就是socket
Channel:一个连接
1、获取当前连接的状态2、配置当前连接参数3、进行read、write、connect、bind操作
Channel 与 EventLoop 是什么关系?
一个 Channel 在它的生命周期内只注册一个 EventLoop
一个 EventLoop 可能会被分配给一个或者多个 Channel
Channel 与 ChannelPipeLine 是什么关系?
一个 Channel 包含了一个 ChannelPipeLine,而 ChannelPipeLine 中又维护了一个由 ChannelHandlerContext 组成的双向链表;这个链表的头是 HeadContext,链表的尾是 TailContext,并且一个 ChannelHandlerContext 中又关联着一个 ChannelHandler
0 条评论
回复 删除
下一页