Netty框架
2022-01-10 17:01:15 338 举报
AI智能生成
登录查看完整内容
Netty知识总结
作者其他创作
大纲/内容
事件从head传播到tail
ChannelHandlerContext
HeadContext
head
ctx.fireChannelRead()
ChannelInboundHandler.channelRead()
next
next.invokeChannelRead()
fireChannelRead()
head.invokeChannelRead()
DefaultChannelPipeline.fireChannelRead()
AbstractNioByteChannel.read()
InBound
ctx.write()
ChannelOutboundHandler.write()
invokeWrite0
write()
next.invokeWrite()
ChannelHandlerContext.write()
从当前Handler传播到head
ChannelPipeline.write()
Channel.write()
TailContext
tail.write()
从tail传播到head
OutBound
事件传播
8K
order-0 block
16 K
order-1 block
32 K
order-2 block
...
16M
order-11 block
PoolChunk
一大块连续的内存
PoolArea
数据结构
Netty中ByteBuf内存泄露及释放解析
参考
Netty的接收和发送ByteBuf采用的DirectBuffers,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝
如果使用传统的堆内存(heap buffers)进行socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入socket中。相比堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
先申请一块大内存池,在内存池中分配空间,对于这种应用级别的内存二次分配,就需要手动对池化的ByteBuf进行释放,否则就有可能出现内存泄露的问题。
PooledHeapByteBuf
PooledDirectByteBuf
abstractPooledByteBuf
ChannelHandlerContext.alloc()
PooledByteBufAllocator
池化
每次I/O读写都会创建一个新ByteBuf,频繁进行大块内存的分配和回收对性能有一定影响,非池化的ByteBuf可以通过JVM GC自动回收,也推荐手动回收UnpooledDirectByteBuf等使用堆外内存的ByteBuf
Java堆内存
推荐业务数据的内存分配使用
UnpooledHeapByteBuf
UnpooledDirectByteBuf
Unpooled.directBuffer()
Unpooled.buffer()
Unpooled.copiedBuffer()
Unpooled
UnpooledByteBufAllocator
非池化
类图
创建
高位字节序:高位字节在前,低位字节在后(内存地址低位在前,高位地址在后)。
低位字节序:低位字节在前,高位字节在后(内存地址低位在前,高位地址在后)。
netty中默认字节序是大端字节序,即字节高位在前,低位在后,符合人类的书写习惯。
字节序
readInt()
writeInt()
大端字节序
readIntLE()
writeLE()
小端字节序
order()
把已读的区域数据给丢弃掉,其实就是把这部分区域给回收了,把读写索引都往前移,可写区域就大了
discardReadBytes()
这个并不是清除数据,只是重置读写索引到0,可写区域又变大了
clear()
完整拷贝,与原buffer是两份数据
返回ByteBuf的可读字节的拷贝。修改返回的ByteBuf内容与当前ByteBuf完全不会相互影响。此方法不会修改当前ByteBuf的readerIndex或writerIndex
copy()
浅拷贝,与原buffer是共享数据的
duplicate()
slice()
复制与共享
引用计数减少1
release()
引用计数增加1
retain()
获取引用计数
refCnt()
引用计数相关ReferenceCounted
常用方法
发送组件将ByteBuf传递给接收组件,发送组件一般不负责释放,由接收组件释放
如果一个组件除了接收处理ByteBUf,而不做其他操作(比如再传给其他组件),这个组件负责释放ByteBuf。
谁最后访问ByteBuf,谁最后负责释放
释放原则
如果ChannelHandler中,只有处理ByteBuf的操作,不会调ctx.fireChannelRead(buf)把ByteBuf传递下去,那就要在这个ChannelHandler中释放ByteBuf。
如果ChannelHandler中,会调ctx.fireChannelRead(buf)把ByteBuf传递给下一个ChannelHandler,那在当前ChannelHandler中不需要释放ByteBuf,由最后一个使用该ByteBuf的ChannelHandler释放。
如果处理的ByteBuf是由decode()等会增加计数器的操作生成的,不再传递时,ByteBuf也要释放。
如果不确定要不要释放,或者简化释放的过程,可以调用ReferenceCountUtil.release(ByteBuf)函数。
也可以把ChannelHandler都承继自SimpleChannelInboundHandler虚类,该类会在channelRead函数中调用ReferenceCountUtil.release(msg)来帮助释放ByteBuf
在 ChannelHandler负责链中,如何释放
请求ByteBuf,调用fireChannelRead()传播,由TailContext释放。
Handler继承SimpleChannelInboundHandler可在ChannelRead中自动释放
自动释放
如果中途没有使用fireChannelRead传递下去也要自己释放
通过Channel或ChannelHandlerContext创建的但是没有传递下去的ByteBuf也要手动释放
手动释放
请求 ByteBuf
响应ByteBuf都是由应用程序产生的,由Netty负责释放。
响应 ByteBuf
释放时机
ByteBuf
ResourceLeakDetector
完全禁用内存泄露检测,不推荐
DISABLE
抽样1%的ByteBuf,提示是否有内存泄露
SIMPLE(默认)
抽样1%的ByteBuf,提示哪里产生了内存泄露
ADVANCED
对每一个ByteBu进行检测,提示哪里产生了内存泄露
PARANOID
检测级别
java -Dio.netty.leakDetectionLevel=ADVANCED
参数设置
内存泄露监测
内存管理
threadLocalDirectBuffer()
ByteBufUtil
safeRelease(ByteBuf)
release(ByteBuf)
ReferenceCountUtil
工具类
Netty如何管理资源
业务逻辑如何处理消息
消息资源管理
非阻塞传输,基于java.nio
NIO
非阻塞传输,基于JNI的epoll,只支持linux,性能优于NIO。linux-2.5.44(2002)+
Epoll
阻塞传输,基于java.net
OIO
本地传输,在VM内部通过管道通讯
Local
测试使用
Embedded
特定用例特定传输的各种因素和场景
发送消息方式
Channel.close()
nextContextOutbound.close()
TailContext.close()
ChannelPipeline.close()
ChannelHandlerContext.close()
Channel关闭方式
入站接收缓冲区
ChannelOutboundBuffer
将需要写的 ByteBuff 存储到 ChannelOutboundBuffer 中
write 操作
从 ChannelOutboundBuffer 中将需要发送的数据读出来,并通过 Channel 发送出去。
flush 操作
出站写缓冲区
write操作后,等待发送消息链表增加后,长度大于高水位线,channel被设置为不可写(unwritable)
默认: 64 * 1024 个消息
ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK
如果在write()时候,没有做判断 channel.isWritable() 的,就跟没设置一样!!!
ChannelInboundHandler.channelWritabilityChanged()
Channel 可写状态变化通知回调方法
高水位
flush操作时前,等待发送消息链表减少后,长度小于低水位线,channel被设置为可写(writable)
默认: 32 * 1024 个消息
ChannelOption.WRITE_BUFFER_LOW_WATER_MARK
低水位
默认: 4M
maxWriteSize
默认: 400M
maxGlobalWriteSize
默认: 4s
maxWriteDelay
写操作配置
Netty 中的粘包和拆包
TCP是以流动的方式传输数据,传输的最小单位为一个报文段(segment)
Socket 的内核发送缓冲区(SO_SNDBUF )
Socket 的内核接收缓冲区(SO_RCVBUF)
滑动窗口
Socket 缓冲区与滑动窗口
最大传输单元Maxitum Transmission Unit
链路层对一次可以发送的最大数据的限制
路由器MTU配置
操作系统MTU配置
MTU
最大分段大小Maxitum Segment Size
TCP 报文中 data 部分的最大长度,是传输层对一次可以发送的最大数据的限制。
MSS = MTU(1500) -IP Header(20 or 40)-TCP Header(20)
MSS
Nagle 算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块
Nagle 算法的规则
Nagle算法
出现原因
TCP粘包/拆包
实现了控制读取速度、判断发送数据需要的时间、统计周期内读写的字节
单个Channel
ChannelTrafficShapingHandler
所有Channel
GlobalTrafficShapingHandler
GlobalChannelTrafficShapingHandler
计算读取和发送的等待时间以及监控读取发送的字节数
TrafficCounter
流量整形
重点话题
Future<?>
shutdownGracefully()
NioEventLoopGroup
EventLoopGroup
memberChannelPipeline
NioServerSocketChannel
ServerChannel
initChannel()
abstractChannelInitializer<SocketChannel>
ChannelHandler
ChannelFuture
bind()
ServerBootstrap
控制流、多线程处理、并发
EpollEventLoopGroup
OioEventLoopGroup
EventLoop
EventExecutorGroup
ChannelPipeline
异步通知
sync()
channel()
operationComplete
ChannelFutureListener
ScheduledFuture
Future
启动类
通讯连接通道,线程安全的
非阻塞传输
EpollServerSocketChannel
阻塞传输
OioServerSocketChannel
本地传输
测试
传输类型
NioSocketChannel
SocketChannel
ChannelConfig
config()
pipeline()
eventLoop()
closeFuture()
connect()
read()
写入ChannelPipeline缓存
flush()
method
Channel
EventExecutor
executor()
close()
ByteBufAllocator
alloc()
channelActive()
channelRead()
channelReadComplete()
userEventTriggered()
exceptionCaught()
abstractChannelInitializer<C extends Channel>
类型匹配则自动释放byteBuf引用
channelRead0()
abstractSimpleChannelInboundHandler<I>
abstract:ChannelInboundHandlerAdapter
interface:ChannelInboundHandler
classWriteTimeoutHandler
abstract:ChannelOutboundHandlerAdapter
interface:ChannelOutboundHandler
classReadTimeoutHandler
classIdleStateHandler
abstract:ChannelDuplexHandler
annotation@Sharable
abstractByteToMessageDecoder
abstractMessageToMessageDecoder<I>
解码器
abstractMessageToByteEncoder
abstractMessageToMessageEncoder<I>
编码器
编解码器
数据粘包/拆包处理
加解密处理
业务流程处理
分类
分层处理,网络处理和业务处理分离
优点
Netty框架
0 条评论
回复 删除
下一页