Netty知识体系
2024-06-07 20:11:18 0 举报
AI智能生成
Netty知识体系
作者其他创作
大纲/内容
重要组件
Bootstrap、ServerBootstrap
Future、ChannelFuture
Channel
通信组件,能够用于执行网络 I/O 操作
子主题 2
Selector
Netty 基于 Selector 对象实现 I/O 多路复用,通过 Selector 一个线程可以监听多个连接的 Channel 事件。
当向一个 Selector 中注册 Channel 后,Selector 内部的机制就可以自动不断地查询(Select) 这些注册的 Channel 是否有已就绪的 I/O 事件(例如可读,可写,网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个 Channel
NioEventLoop
维护了一个线程和任务队列,支持异步提交执行任务,线程启动时会调用 NioEventLoop 的 run 方法,执行 I/O 任务和非 I/O 任务
I/O 任务
selectionKey 中 ready 的事件,如 accept、connect、read、write 等,由 processSelectedKeys 方法触发。
非I/O 任务
添加到 taskQueue 中的任务,如 register0、
bind0 等任务,由 runAllTasks 方法触发。
NioEventLoopGroup
主要管理 eventLoop 的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NioEventLoop)负责处理多个 Channel 上的事件,而一个 Channel 只对应于一个线程
ChannelHandler
ChannelHandler 是一个接口,处理 I/O 事件或拦截 I/O 操作,并将其转发到其 ChannelPipeline(业务处理链)中的下一个处理程序
子类
ChannelInboundHandler
ChannelOutboundHandler
ChannelHandlerContext
保存 Channel 相关的所有上下文信息,同时关联一个 ChannelHandler 对象
ChannelPipline
一个 Channel 包含了一个 ChannelPipeline,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler
ByteBuf
分支主题
先写再度,已读取过的数即可丢弃
对比NIO的ByteBuffer
参数
position
limit
capacity
FastThreadLocal
为什么比Java自带的ThreadLocal 快
FastThreadLocal 使用 Object 数组替代了 Entry 数组,Object [0] 存储的是一个 Set<FastThreadLocal<?>> 集合,从数组下标 1 开始都是直接存储的 value 数据,不再采用 ThreadLocal 的键值对形式进行存储
ThreadLocalMap 采用线性探测法解决 Hash 冲突导致性能较慢,而 FastThreadLocal 初始化的时候分配一个数组索引 index,index 的值采用原子类 AtomicInteger 保证顺序递增,通过调用 InternalThreadLocalMap.nextVariableIndex () 方法获得
线程模型
分支主题
分支主题
分支主题
分支主题
零拷贝
常用技术
mmap
分支主题
sendFile
Linux 2.1
分支主题
仍有一次CPU Copy
Linux 2.4
分支主题
真正的零拷贝
定义
指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。它的作用是在数据报从网络设备到用户程序空间传递的过程中,减少数据拷贝次数,减少系统调用,实现 CPU 的零参与,彻底消除 CPU 在这方面的负载。实现零拷贝用到的最主要技术是 DMA 数据传输技术和内存区域映射技术。
netty中的体现
DirectByteBuf
使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。
Composite Buffers
传统的ByteBuffer,如果需要将两个ByteBuffer中的数据组合到一起,我们需要首先创建一个size=size1+size2大小的新的数组,然后将两个数组中的数据拷贝到新的数组中。但是使用Netty提供的组合ByteBuf,就可以避免这样的操作,因为CompositeByteBuf并没有真正将多个Buffer组合起来,而是保存了它们的引用,从而避免了数据的拷贝,实现了零拷贝
FileChannel.transferTo
发送消息的两种方式
1. 使用channel直接发送
消息会经过pipeline中的所有handler处理器
2. 使用ChannelHandler中的ChannelHandlerContext发送
消息仅会从当前handler的下一个handler开始处理
TCP粘包拆包
出现原因
1. 应用程序写入的字节大小大于套接字发送缓冲区大小
2. 待发送的数据大于最大报文长度(MSS,TCP报文段中的数据字段部分的最大长度),传输前需要拆包
3. 以太网的payload大于最大传输单元(MTU),需要进行IP分片
解决方式
1. 固定报文长度 FixedLengthFrameDecoder
2. 自定义分隔符 DelimiterBaseFrameDecoder
同类型框架
Mina
Grizzly
Netty解决NIO的bug
NIO epoll 空轮询
原因
若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%
解决方式
对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,
若在某个周期内连续发生N次空轮询,当selectCnt 达到阈值(默认512),则触发了epoll死循环bug
重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上移除,重新注册到新的Selector上,并将原来的Selector关闭。
NIO非阻塞编程原理
1. 当客户端连接时,通过ServerSocketChannel得到socketchannel
2. 将socketChannel注册到selector上 (register方法),
一个selector可以注册多个SocketChannel
3. 注册后返回一个SelectionKey
4. Selector监听select方法,返回有事件发生的通道个数
5. 遍历各个通道个数,再通过SelectionKey反向获取SocketChannel ( 方法channel)
6. 通过channel开始处理业务逻辑
Selector
分支主题
线程模型
传统阻塞IO模型
Reactor模型
单Reactor单线程
连接、读取及发送都使用一个线程处理
优势
模型简单,无多线程、进程通信等竞争问题
劣势
只有一个线程,无法发挥多核CPU的性能
Handler在处理某个连接业务时,整个进程无法处理其他连接事件,容易导致性能瓶颈
分支主题
处理流程
1. 通过select阻塞对象监听多路连接请求,收到事件后通过dispatcher分发
2. 若是连接事件则由Acceptor通过Accept处理连接请求,然后创建Handler
对象处理连接完成后的业务处理
3. 若不是连接事件,则调用连接对应的Handler响应事件
4. Handler会完成: Read ->业务处理 -> Send 的完整业务流程
单Reactor多线程
分支主题
处理l流程
1. Reactor通过select监控客户端请求事件,收到事件后,通过dispatch进行分发
2. 若是连接请求,则Acceptor通过accept方法处理连接请求,然后创建一个Handler对象处理完成连接后的各种事件
3. 若不是连接请求,则由reactor分发调用连接对应的handler来处理
4. handler只负责响应事件, 不做具体业务处理。通过read读取数据后,分发给worker线程池的某个线程处理
5. worker线程池会分配独立线程完成真正的业务,并将结果返回给handler
6. handler收到响应后,通过send将结果返回给client
优点
1. 可利用多核CPU
缺点
1. 多线程数据共享及访问较复杂
2. reactor单线程运行,负责所有事件的监听及响应,在高并发场景容易出现性能瓶颈
主从Reactor多线程(多个MainReactor对应多个SubReactor)
分支主题
处理流程
1. 主线程MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接事件
2. 当Acceptor处理连接事件后,MainReactor将连接分配给SubReactor
3. SubReactor将连接加入到连接队列进行监听,并创建handler进行各种事件处理
4. 当有事件发生时,subreactor就会调用对应的handler处理
5. handler通过read读取数据,分发给后面的worker线程处理
6. worker线程池分配独立的worker线程进行业务处理,并返回结果
7. handler收到响应结果后,通过send将结果返回给client
8. Reactor主线程可对应多个Reactor子线程
相关资料
http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf
编码解码器
netty自身不足
1. 无法跨语言
2. 编码解码效率低
底层使用Java序列化技术,效率不高,体积过大
protobuf
跨语言
性能高
分支主题
TCP粘包拆包
自定义消息协议,指定数据包长度,服务端接收后即可按照长度解码
分支主题
IO流比较
BIO
面向流数据,要么是输入流要么是输出流,不可同时兼备输入输出
NIO
面向块或缓冲区, 支持双向读写
Buffer
三个重要属性
position
limit
capacity
Buffer
Java NIO
DirectByteBuffer
MappedByteBuffer
ByteBuffer
堆内缓冲区,受GC控制
缺陷
长度固定,无法扩容
读写切换过程需要执行flip操作,若不执行则报错
Netty
ByteBuf
优势
1. 自动扩容(4M阀值)
当申请的新空间大于阀值时,采用每次步进4MB的方式进行扩容,而不是倍增,因为这会造成内存膨胀和浪费
申请的新空间小于阀值时,则以64为基数进行倍增而不是步进,因为当内存比较小的时候,倍增是可以接受的
2. 读写模式由内部两个读写索引维护,无需flip
多种实现
Heap ByteBuf
Direct ByteBuf
CompositeByteBuf
传统数据拷贝
分支主题
0 条评论
下一页