Netty核心知识整理
2022-11-10 14:59:48 24 举报
AI智能生成
登录查看完整内容
Netty核心知识整理
作者其他创作
大纲/内容
Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
Netty是基于nio的,它封装了jdk的nio
使用场景:公司项目定制私有化协议/自己的开源项目中参考RocketMQ对Netty的使用定制的协议
Netty是什么
每个NioEventLoop对应一个线程和一个Selector,NioServerSocketChannel会主动注册到某一个NioEventLoop的Selector上,NioEventLoop负责事件轮询。
epoll空轮训 cpu 100%的bug解决
NIOEventLoopGroup源码?
Channel 为 Netty 网络操作(读写等操作)抽象类,每个NioEventLoop对应一个线程和一个Selector,NioServerSocketChannel会主动注册到某一个NioEventLoop的Selector上,NioEventLoop负责事件轮询。
1 Chanel和EventLoop的关系?
channelHandler是消息的具体处理器,负责处理读写操作,客户端连接等。channelPipeline是handler的链,提供了一个用于沿着链传播的入站和出站的事件流的API。当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。
2 channelHandler和ChannelPipeline之间的关系?
EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含一个线程)EventLoop 的主要作用实际就是负责监听网络事件并调用事件处理器进行相关 I/O 操作的处理。
Boss EventloopGroup 用于接收连接,Worker EventloopGroup 用于具体的处理(消息的读写以及其他逻辑处理)。当客户端通过 connect 方法连接服务端时,bossGroup 处理客户端连接请求。当客户端处理完成后,会将这个连接提交给 workerGroup 来处理,然后 workerGroup 负责处理其 IO 相关操作。
3 EventloopGroup 了解么?和 EventLoop 啥关系?
DEFAULT_EVENT_LOOP_THREADS 的值为CPU核心数*2
NioEventLoopGroup 默认的构造函数会起多少线程?
重要组件
支持自动扩容(4M),保证put方法不会抛出异常、通过内置的复合缓冲类型,实现零拷贝(zero-copy);
不需要调用flip()来切换读/写模式,读取和写入索引分开;方法链;
引用计数基于AtomicIntegerFieldUpdater用于内存回收;
PooledByteBuf采用二叉树来实现一个内存池,集中管理内存的分配和释放,不用每次使用都新建一个缓冲区对象。UnpooledHeapByteBuf每次都会新建一个缓冲区对象
ByteBuf
read:用户态 -> 内核态 && 磁盘数据 -> DMA引擎拷贝 -> 内核缓冲区
内核态 -> 用户态&&Socket缓冲区数据 -> DMA引擎拷贝 -> 网络协议引擎
write:用户态 -> 内核态 && 用户缓冲区数据 -> CPU拷贝 -> Socket缓冲区
即普通的IO操作,需要执行四次内核态切换 && 四次数据拷贝
File file = new File(\"XXX.txt\
普通IO操作
直接将磁盘文件数据映射到内核缓冲区,这个映射过程是基于DMA拷贝的
同时用户缓冲区是跟内核缓冲区共享一块映射数据的
建立共享映射之后,就不需要从内核缓冲区拷贝到用户缓冲区了
即减少了一次数据拷贝
mmap内存映射技术
基于linux提供的sendfile,也就是零拷贝技术
read:用户态 -> 内核态 , 磁盘数据 -> DMA拷贝 -> 内核缓冲区,同时从内核缓冲区拷贝一些offset和length到Socket缓冲区
这个offset和length的量很少,几乎可以忽略不计
零拷贝
具体
在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据。而在 Netty 层面 ,零拷贝主要体现在对于数据操作的优化。
vs OS zero-copy
https://zhuangxiaoyan.blog.csdn.net/article/details/116447618
参考链接TODO
Netty零拷贝
1、概念:IO多路复⽤是指内核⼀旦发现进程指定的⼀个或者多个IO条件准备读取,它就通知该进程
2、优势:与多进程和多线程技术相⽐,I/O多路复⽤技术的最⼤优势是系统开销⼩,系统不必创建进程/线程,也不必维护这些进程/线程,从⽽⼤⼤减⼩了系统的开销。
select的⼀个缺点在于单个进程能够监视的⽂件描述符的数量存在最⼤限制,在Linux上⼀般为1024,可以通过修改宏定义甚⾄重新编译内核的⽅式提升这⼀限制,但是这样也会造成效率的降低。
其良好跨平台⽀持也是它的⼀个优点
select:select⽬前⼏乎在所有的平台上⽀持。
poll:它没有最⼤连接数的限制,原因是它是基于链表来存储的,但是同样有⼀个缺点:a. ⼤量的fd的数组被整体复制于⽤户态和内核地址空间之间,⽽不管这样的复制是不是有意义。b. poll还有⼀个特点是“⽔平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
epoll跟select都能提供多路I/O复⽤的解决⽅案。在现在的Linux内核⾥有都能够⽀持,其中epoll是Linux所特有,⽽select则应该是POSIX所规定,⼀般操作系统均有实现。
3、系统:⽬前⽀持I/O多路复⽤的系统调⽤有 select,pselect,poll,epoll。
IO多路复用
NIOEventLoopGroup 源码?
Netty 的零拷贝实现?
Netty的特点
参考链接:https://zhuanlan.zhihu.com/p/283055781
思路:RabbitMQ的扩容很鸡肋,kafka扩容会有平滑过渡的问题,最终采用阿里云版本的RocketMQ
如何做到数百万台车联网设备同时在线 0 故障
Netty性能调优
服务端客户端启动流程
Block IO , 顾名思义 , 同步阻塞 原理:服务器通过一个 Acceptor 线程,负责监听客户端请求和为每个客户端创建一个新的线程进行链路处理。若客户端数量增多,频繁地创建和销毁线程会给服务器打开很大的压力。
如果有大量客户端的时候,那么服务端的线程数量可能达到几千、几万甚至几十万 然后服务器OOM
改良为用线程池的方式代替新增线程,被称为伪异步 IO 。
但是高并发又会有各种排队和延时的问题
弊端:典型的一请求一应答模式
BIO
IntBuffer、LongBuffer、CharBuffer等很多种针对基础数据类型的Buffer
Buffer缓冲区,将数据写入Buffer中,然后从Buffer中读取数据Buffer缓冲区
Channel,NIO中都是通过Channel来进行数据读写的
selector会不断轮询注册的channel,如果某个channel上发生了读写事件,selector就会将这些channel获取出来
一个Selector就通过一个线程,就可以轮询成千上万个channel,这就意味着你的服务端可以介入成千上万的客户端
Selector,多路复用器
New IO , 同步非阻塞,基于Reactor模型
NIO的优化思想就是一个请求一个线程,只有某个客户端发送了一个请求的时候,才会启动一个线程来处理
处理时还是要先读取数据,处理,再返回的,这是个同步的过程
IO磁盘操作时需不断的while询问CPU是否处理完成
核心即非阻塞,selector一个线程就可以不停的轮询缠你了,所有客户端请求都不会阻塞,直接就会进来,大不了排下队
NIO
每个连接发送过来的请求,都会绑定一个buffer,然后通知操作系统去一部完成读
此时你的程序是回去干别的事儿的,等操作系统完成数据读取后,就会回调你的接口,给你操作系统异步读完的数据
然后你对这个数据处理一下,接着将结果往回写
写的时候也是个操作系统一个buffer,让操作系统自己获取数据去完成写操作,写完以后再回来通知你
Async IO, 异步非阻塞,基于Proactor模型
读取数据的时候,提供给操作系统一个buffer,空的,然后你就可以干别的事儿了,把读数据的事儿交给系统去干
内核读数据将数据放入buffer,完事了,来回调你的一个接口,告诉你说,ok,数据读好了
工作线程(读写同理)
AIO
BIO 用BIO的流读写文件时,你发起个IO请求直接hang死,必须等搞完了这次IO才能返回 这个针对的是磁盘文件的IO读写 FileInputStream ,BIO,卡在那儿,直到你读写完成了才可以
NIO 通过NIO的FileChannel发起个文件IO操作,其实发起之后就返回了,你可以干别的事儿,这就是非阻塞 但是接下来你还得不断地去轮询操作系统,看IO操作完事儿了没有
AIO 通过AIO发起个文件IO操作之后,你立马就可以返回干别的事儿了,接下来你也不用管了 操作系统自己干完了IO之后,告诉你说ok了。 同步就是你自己还得主动去轮询操作系统,异步就是操作系统反过来通知你
BIO 是面向流的,NIO 是面向缓冲区的;BIO 的各种流是阻塞的。而 NIO 是非阻塞的;BIO的 Stream 是单向的,而 NIO 的 channel 是双向的。
再深入理解:https://mp.weixin.qq.com/s/39Q4iG6XGjGp7K_zmlpnpA
BIO vs NIO vs AIO
通过内存池重用 ByteBuf;ByteBuf 的解码保护;优雅停机:不再接收新消息、退出前的预处理操作、资源的释放操作。
Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
volatile 的大量、正确使用;CAS 和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。IO 通信性能三原则:传输(AIO)、协议(Http)、线程(主从多线程)
高效并发编程
NIO的封装和优化
Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。
零拷贝和mmap内存映射,尽量减少不必要的内存拷贝,实现了更高效率的传输
reactor线程模型
串行无锁化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁
链路空闲检测机制,读/写空闲超时机制,idleStateHandler 类 用来检测会话状态
TCP参数配置:SO_RCVBUF 和 SO_SNDBUF:通常建议值为 128K 或者 256K
SO_TCPNODELAY:NAGLE 算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法;
Netty高性能表现在?
要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。
原因
定长消息解码器:FixedLengthFrameDecoder。发送方和接收方规定一个固定的消息长度,不够用空格等字符补全,这样接收方每次从接受到的字节流中读取固定长度的字节即可,长度不够就保留本次接受的数据,再在下一个字节流中获取剩下数量的字节数据。
分隔符解码器:LineBasedFrameDecoder或DelimiterBasedFrameDecoder。LineBasedFrameDecoder是行分隔符解码器,分隔符为\或\\;DelimiterBasedFrameDecoder是自定义分隔符解码器
数据长度解码器:LengthFieldBasedFrameDecoder。将发送的消息分为header和body,header存储消息的长度(字节数),body是发送的消息的内容。
Netty解决方法
TCP 粘包 / 拆包
https://www.jianshu.com/p/8d894e42b6e6
Netty内存池
https://www.jianshu.com/p/3bfe0de2b022
Netty对象池
TCP通过序列号、检验和、确认应答信号、重发控制、连接管理、窗口控制、流量控制、拥塞控制实现可靠性。
https://www.jianshu.com/p/6aac4b2a9fd7
TCP协议如何保证可靠传输
Netty核心知识整理
0 条评论
回复 删除
下一页