🚩Netty

Netty 定义

  • 本质:网络应用程序框架
  • 实现:异步、事件驱动
  • 特性:高性能、可维护、快速开发,可以取代 JDK 的NIO
    • 支持常用应用层协议
    • 解决 TCP 粘包
    • 不会出现 epoll 空轮询导致 CPU 100%
    • FastThreadLocal 替代 ThreadLocal,ByteBuf 替代 ByteBuffer
    • 将 NIO 中 selectedKeys 的 Map 实现替换为数组实现
  • 用途:开发服务器和客户端
  • 主版本:4.1(支持 Android)
  • 场景:Spark、Hadoop、RocketMQ、ElasticSearch、ZooKeeper、Dubbo、Spring5(响应式编程)

核心概念

  • ServerBootstrap,服务器端程序的入口,这是 Netty 为简化网络程序配置和关闭等生命周期管理,所引入的 Bootstrapping 机制。我们通常要做的创建 Channel、绑定端口、注册 Handler 等,都可以通过这个统一的入口,以 Fluent API 等形式完成,相对简化了 API 使用。与之相对应, Bootstrap则是 Client 端的通常入口。
  • Channel:可以理解为读操作或写操作,因此可以被打开或被关闭 。作为一个基于 NIO 的扩展框架,Channel 和 Selector 等概念仍然是 Netty 的基础组件,但是针对应用开发具体需求,提供了相对易用的抽象。
  • EventLoop:只由一个线程驱动,处理一个 Channel中 的所有 I/O 事件。这是 Netty 处理事件的核心机制。例子中使用了 EventLoopGroup。我们在 NIO 中通常要做的几件事情,如注册感兴趣的事件、调度相应的 Handler 等,都是 EventLoop 负责。
  • ChannelFuture,这是 Netty 实现异步 IO 的基础之一,保证了同一个 Channel 操作的调用顺序。Netty 扩展了 Java 标准的 Future,提供了针对自己场景的特有Future定义。
  • ChannelHandler,这是应用开发者放置业务逻辑的主要地方,也是我上面提到的“Separation Of Concerns”原则的体现。每个事件都可以被分发给 ChannelHandler 类中的某个用户实现的方法,实现业务逻辑与网络处理代码分离。
  • ChannelPipeline:它是 ChannelHandler 链条的容器,每个 Channel 在创建后,自动被分配一个 ChannelPipeline。

为什么 Netty 仅支持 NIO?

  1. BIO 在连接数高的时候阻塞等待消耗资源,效率低,连接数低的时候使用 JDK 的就够了,因为实现简单。
  2. Windows AIO 实现成熟,但很少用来做服务器;Linux 常用做服务器,但是 AIO 不成熟,相比较 NIO的性能提升并不明显。

Netty 如何解决 Java NIO 早期版本中的 Epoll空转问题?

epoll 空轮询导致 CPU 100%,Netty 检测空轮询次数大于一定阈值,就会 rebuild 多路复用服务器。

Channel 和 ChannelPipeline

在 Netty 中,Channel 是网络通信的基础,表示一个可靠的网络连接ChannelPipeline 是一系列 ChannelHandler 的链表,用于处理 Channel 上的事件和数据,类似于 Servlet 中的 Filter

EventLoop

EventLoop 是 Netty 中的一个重要组件,用于处理 Channel 上的事件和任务,负责监听网络事件并调用事件处理器进行相关I/O操作。每个 Channel 都会绑定一个 EventLoop**,一个 EventLoop 可以处理多个 Channel 上的事件和任务**。Netty 采用了多种线程模型,如单线程模型、多线程模型、主从线程模型等,根据实际需求选择不同的线程模型可以更好地提高程序的性能和并发性。

Channel 是 Netty 网络操作抽象类,EventLoop负责处理注册到其上的Channel 的 I/O操作。

EventloopGroup包含多个EventLoop(EventLoop:Thread = 1 : 1),它管理着所有的EventLoop的生命周期。

NioEventLoopGroup 默认的构造函数实际会起的线程数为 CPU核心数*2

四种事件类型

  • accept:在有连接请求时触发
  • connect:客户端,连接建立后触发
  • read:可读事件
  • write:可写事件

在 select 后,事件要么处理 accept,要么取消 cancel(catch 异常之后),

🌟三种 Reactor 模型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 1. 单线程 BIO
EventLoopGroup eventGroup = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootStrap.group(eventGroup);
// 2. 多线程 NIO
EventLoopGroup eventGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootStrap.group(eventGroup);
// 3. 主从多线程 AIO
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootStrap.group(bossGroup, workerGroup);
// 关闭服务 chennel.close() key.cancel()
// 先不接活,尽量做完手头的事情
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();

ByteBuf

ByteBuf 是 Netty 中的一个重要组件,用于处理数据的缓冲区。与 JDK 中的 ByteBuffer 相比,ByteBuf 具有更好的可扩展性、更高的性能和更丰富的功能。ByteBuf 支持读写索引分离,内存池化和引用计数等功能,可以更好地管理内存和提高程序的性能。

ByteBuffer 的种类

  • java.nio.HeapByteBuffer:java 堆内存,读写效率较低,受到 GC 的影响
  • java.nio.DirectByteBuffer:直接内存,读写效率高(少一次拷贝),不受 GC 影响,分配效率低

🌟如何处理粘包与拆包?

一次解码器 - 抽象类 ByteToMessageDecoder

ByteBuf -> ByteBuf

提供了一些常见的实现类:

  1. FixedLengthFrameDecoder:定长协议解码器,指定固定的字节数

  2. DelimiterBasedFrameDecoder:分隔符解码器

  3. LineBasedFrameDecoder:行分隔符解码器,识别 \n或\r\n

  4. LengthFieldBasedFrameDecoder:长度编码解码器,将报文划分为header和body

    在处理 protobuf 类型数据时,运用了ProtobufVarint32FrameDecoder,允许 int 长度可变,进一步节约内存。

  5. JsonObjectDecoder:监测到匹配的{}或[]就认为是完整的json对象或json数组

二次解码器 - 抽象类MessageToMessageDecoder

ByteBuf -> Java Object

Netty 序列化相比 JDK 序列化

减少了:

  1. 固定的魔数
  2. 类的元信息,只保留了类名用于反射

Netty 内存使用

内存池方法:get() recycle()

0%