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 中已经实现了,其众多子类采用不同的策略来分配内存空间,下面对重要的几个子类总结如下:

  1. PooledHeapByteBuf :池化的堆内缓冲区
  2. PooledUnsafeHeapByteBuf :池化的Unsafe 堆内缓冲区
  3. PooledDirectByteBuf :池化的直接(堆外)缓冲区
  4. PooledUnsafeDirectByteBuf :池化的Unsafe 直接(堆外)缓冲区
  5. UnpooledHeapByteBuf :非池化的堆内缓冲区
  6. UnpooledUnsafeHeapByteBuf :非池化的Unsafe 堆内缓冲区
  7. UnpooledDirectByteBuf :非池化的直接(堆外)缓冲区
  8. UnpooledUnsafeDirectByteBuf :非池化的Unsafe 直接(堆外)缓冲区

读指针/写指针 就不介绍了 ByteBuf的操作都是操作指针得来的

池化:类似于线程池的机制,提高效率

池化接口:ByteBufAllocator

      获取池化接口  channel.alloc()或channelHandleContext.alloc()

       通过此接口可构建ByteBuf

非池化工具类: UnPooled 通过其静态方法可以申请ByteBuf()

直接内存/堆外内存  ByteBuf在堆外内存传输的时候可以少一次复制

堆内存

经验表明,Bytebuf的最佳实践是在IO通信线程的读写缓冲区使用DirectByteBuf,后端业务使用HeapByteBuf。

有些情况下需要对ByteBuf进行释放

释放代码 RefereceCountUtil.safeRelease(ByteBuf byt)

posted @ 2021-01-08 11:22  会潮  阅读(143)  评论(0)    收藏  举报