Netty是什么,为什么要用Netty

Netty是一款基于NIO开发的网络通信框架,在易用的同时,没有丧失可维护性和性能等优势。

特点:高并发(NIO)、传输快(零拷贝)、封装好。

零拷贝是什么

零拷贝主要体现在三个方面:

  1. Netty的接受和发送ByteBuffer采用Direct Buffers,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。
  2. 提供了组合Buffer对象,可以聚合多个ByteBuffer对象,避免了传统通过内存拷贝将几个小buffer合并成一个大Buffer。
  3. Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

Netty有什么组件

Bootstrap: 引导类,提供一个用于应用程序网络层配置的容器。

Channel:底层网络传输API必须提供给I/O操作的接口。

ChannelHandler:channelHandler支持很多协议,并且提供用于数据处理的容器。

ChannelInboundHandler:这个类型接收到入站事件(包括接收到的数据)可以处理应用程序逻辑

ChannelPipeline:提供一个容器给channelHandler链并提供一个API用于管理沿着链入站和出站事件的流动。每个Channel都有自己的ChannelPipeline,是当Channel创建时自动被创建的。

EventLoop:用于处理I/O操作。一个单一的EventLoop通常会处理多个Channel事件。

EventLoopGroup:可以含有多于一个的EventLoop和提供一个迭代用于检索清单中的下一个。

ChannelFuture:Netty所有的I/O操作都是异步的。因为一个操作可能无法立即返回,我们需要有一种方法在以后确定这个操作的返回结果。出于这个目的,Netty提供了接口ChannelFuture,它的addLiListener方法注册了一个ChannelFutureListener,当操作完成时,可以被通知。

关系:

  1. Socket和Channel是一对一。
  2. Channel和EventLoop是多对一。
  3. EventLoop和EventLoopgroup是多对一。
  4. EventLoop和Thread是一对一。
  5. ChannelHandler和ChannelPipeline是多对一。
  6. ChannelPipeline和Channel是一对一。

粘包和半包问题

粘包:发送方每次写入数据<套接字缓冲区的大小;接收方读取数据不及时。

半包:发送方每次写入数据>套接字缓冲区的大小;发送的数据大于协议的最大传输单元,必须拆包。

根本原因:TCP 是流式协议,消息无边界。

解决方法:

  1. 改成短连接(不推荐)
  2. 封装成帧(固定长度、分隔符、专门的length字段) | 方式 | 解码 | 编码 | | —- | —- | —- | | 固定长度 | FixedLengthFrameDecoder | / | | 分隔符 | DelimiterBasedFrameDecoder | / | | 专门的length字段 | LengthFieldBasedFrameDecoder | LengthFieldPrepender |

Netty的序列化问题

  1. XML(较差)
  2. JSON
  3. Fastjson
  4. Thrift
  5. Avro(优秀)
  6. Protobuf(优秀)

Netty线程模型

Netty结合了Selector和Reactor模式设计了高效的线程模型,主要角色包括Selector、EventLoopGroup/EventLoop、ChannelPipeline。

首先,Selector是一个多路复用器,负责将就绪的IO事件分离出来,BossEventLoopGroup会负责接收,通常是一个单线程,根据事件分发到WorkerEventLoopGroup,随后通过ChannelPipeline将请求进行处理,ChannelPipeline内部是一个ChannelHandler链表,负责具体的处理。

优雅退出

  1. 把NIO线程的状态位设置成ST_SHUTTING_DOWN状态,不再处理新的消息(不允许再对外发送消息);
  2. 退出前的预处理操作:把发送队列中尚未发送或者正在发送的消息发送完、把已经到期或者在退出超时之前到期的定时任务执行完成、把用户注册到NIO线程的退出Hook任务执行完成;
  3. 资源的释放操作:所有Channel的释放、多路复用器的注册和关闭、所有队列和定时任务的清空取消,最后是NIO线程的退出。

内存分配

主要分为Arena、ChunkList、Chunk、Page、Subpage这5个层级。

  1. 需要分配的内存小于PageSize时,分配tiny或者 small内存。
  2. 需要分配的内存介于PageSize和 ChunkSize时,则分配normal内存。
  3. 需要分配的内存大于ChunkSize时,则分配huge内存(非池化内存)。