netty杂记
1.重要概念
Channel
Netty中的channel定义了丰富的和socket交互的操作方法:bind, close, config, connect, isActive, isOpen, isWritable, read, write 等等
ChannelHandler
用户处理入站和出站的数据。常用接口是 ChannelInboundHandler/ ChannelOutboundHandler,表示接收到入站/出站事件
ChannelPipeline
ChannelPipeline 提供了一个容器给 ChannelHandler 链并提供了一个API 用于管理沿着链入站和出站事件的流动。每个 Channel 都有自己的ChannelPipeline,在 Channel 创建时自动创建的。
ChannelFuture
用于监听Netty的处理结果,并根据结果进行后续处理。
EventLoop
EventLoop用于处理Channel的 I/O 操作的线程,一个单一的EventLoop通常处理多个Channel事件,一个EventLoopGroup包含一个或多个EventLoop
在netty启动类中能看到上面这些的身影
2. netty网络编程
Netty已支持多种协议 如http,mqtt.提供了编解码器和消息实体类,在实现自定协议时则需要自行实现编解码器和消息实体类(实体类的定义要能方便的获取到消息的各种协议属性).
方法定义:
可读:channelRead、链接到来:channelActive、链接断开:channelInactive、异常:exceptionCaught
服务端ChannelPipeline中的handle处理链顺序应为(个人理解):
心跳handle –> 拆包handle –> 解码 handle –> 业务处理handle –> 编码handle
2.1心跳handle

参数含义:
1)readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)
2)writerIdleTime: 为写超时时间(即测试端一定时间内向被测试端发送消息)
3)allIdleTime:所有类型的超时时间
可以直接使用它或者继承它 重写channelIdle方法 在里面写超时逻辑,示例代码如下

2.2拆包handle
Netty提供的拆包handle有
(1)FixedLengthFrameDecoder 定长解码器
(2)LineBasedFrameDecoder和StringDecoder回车换行符作为消息结束符的TCP拆包
(3)DelimiterBasedFrameDecoder 特殊分隔符解码器
(4)LengthFieldBasedFrameDecoder 自定义长度解码器(自定协议,数据长度在协议消息头等情况,具体百度)
2.3编码解码器
底层编码器就是把消息实体类对象转换成ByteBuf类型

解码器就是把ByteBuf类型转换成消息实体类对象,但涉及到BuytBuf指针,我还没弄明白,大体对应的就是
in.readShort()
in.readByte()
in.readByte()……
2.4 handle传递到下一个handle
一个ChannelHandler只能通过唤醒ChannelHandlerContext中event繁殖的方法,来将一个event递交给它的下一个handler。这些方法包括:
Inbound event
ChannelHandlerContext.fireChannelRegistered()
ChannelHandlerContext.fireChannelActive()
ChannelHandlerContext.fireChannelRead(Object)
ChannelHandlerContext.fireChannelReadComplete()
ChannelHandlerContext.fireExceptionCaught(Throwable)
ChannelHandlerContext.fireUserEventTriggered(Object)
ChannelHandlerContext.fireChannelWritabilityChanged()
ChannelHandlerContext.fireChannelInactive()
Outbound
ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
ChannelHandlerContext.write(Object, ChannelPromise)
ChannelHandlerContext.flush()
ChannelHandlerContext.read()
ChannelHandlerContext.disconnect(ChannelPromise)
ChannelHandlerContext.close(ChannelPromise)
3.channelHandle线程安全

一个channel只在一个EventLoop线程中,不存在并发,所以在业务channelHandle中调用channelHandlerContext或channel的read write方法是线程安全的;
每一个新创建的Channel都将会被分配一个新的ChannelPipeline,但其中的handler未必是,handle是可以被共享的,如下客户端代码

客户端发起的8个连接就共用了一个handle,可能会有线程安全问题,并且必须要在handle定义上加上@Sharable 注解,若改成ch.pipeline().addLast(new Handle()),则是不同实例不用考虑线程安全
(个人理解,正确与否未知)对于服务端每个连接都是一个channel,每个channel一个ChannelPipeline,多个channel可能对应则多个EventLoop线程,而自定义业务handle在ChannelPipeline中运行,所以handle是被共享着运行在多线程环境下,需要加Sharable注解且通过原之类/读写锁等方式保证线程安全
4.ByteBuf
ByteBuf 最基本的读写API 操作在AbstractByteBuf 中已经实现了,其众多子类采用不同的策略来分配内存空间,下面对重要的几个子类总结如下:
- PooledHeapByteBuf :池化的堆内缓冲区
- PooledUnsafeHeapByteBuf :池化的Unsafe 堆内缓冲区
- PooledDirectByteBuf :池化的直接(堆外)缓冲区
- PooledUnsafeDirectByteBuf :池化的Unsafe 直接(堆外)缓冲区
- UnpooledHeapByteBuf :非池化的堆内缓冲区
- UnpooledUnsafeHeapByteBuf :非池化的Unsafe 堆内缓冲区
- UnpooledDirectByteBuf :非池化的直接(堆外)缓冲区
- UnpooledUnsafeDirectByteBuf :非池化的Unsafe 直接(堆外)缓冲区
读指针/写指针 就不介绍了 ByteBuf的操作都是操作指针得来的
池化:类似于线程池的机制,提高效率
池化接口:ByteBufAllocator
获取池化接口 channel.alloc()或channelHandleContext.alloc()
通过此接口可构建ByteBuf
非池化工具类: UnPooled 通过其静态方法可以申请ByteBuf()
直接内存/堆外内存 ByteBuf在堆外内存传输的时候可以少一次复制
堆内存
经验表明,Bytebuf的最佳实践是在IO通信线程的读写缓冲区使用DirectByteBuf,后端业务使用HeapByteBuf。
有些情况下需要对ByteBuf进行释放
释放代码 RefereceCountUtil.safeRelease(ByteBuf byt)

浙公网安备 33010602011771号