Netty 是一个异步事件驱动的通信框架,可用于搭建高性能协议服务器和客户端。

一、NIO

Selector机制是NIO的核心:

  1. 当客户端请求时,就创建一个scoketChannel,并注册到Selector上(多路复用器)
  2. Selector关注服务端IO读写事件,客户端不需要等待IO事件完成,可继续完成接下来的流程。
  3. 一旦服务端完成了io的读写操作,selector会接收到通知,并告知客户端IO操作已完成。
  4. 客户端接收到通知后,即可通过socketChannel获得所需数据。

二、Netty的特点

  1. 对NIO进行封装,让开发者不需关注NIO底层的原理,调用netty组件即可完成开发工作。
  2. 对网络的调用透明,socket建立TCP连接、网络异常处理等都进行了封装。
  3. 对数据的处理灵活,支持多种序列化框架,并通过ChannelHandler机制,可以自定义编码/解码器
  4. 对性能调优友好,提供线程池模式以及buffer重用机制。

三、Netty 核心组件

核心组件主要用于服务在数据传输时候,产生事件,并且对事件进行监控和处理。

1、channel

客户端和服务端连接的时候会建立一个 Channel。这个 Channel 我们可以理解为 Socket 连接,它负责基本的 IO 操作,例如:bind(),connect(),read(),write()。同时数据也是入站和出站的载体。Channel 连接服务,让信息之间可以流动。

2、EventLoop 和 EventLoopGroup

服务发出的消息称作“出站”消息,服务接受的消息称作“入站”消息。v那么消息的“出站”/“入站”就会产生事件(Event)。这些事件包括连接已激活,数据读取,异常事件,打开链接,关闭链接等等。

数据的流动产生事件,EventLoop用于监控和协调事件,它的主要工作就是事件的发现以及通知。每个 Channel 都会被分配到一个 EventLoop,一个 EventLoop 可以服务于多个 Channel,即 EventLoop 与 Channel是一对多的关系。每一个EventLoop 会占用一个 Thread,同时这个 Thread 会处理 EventLoop 上面发生的所有 IO 操作和事件。

当客户端发送消息到服务端,EventLoop发现后告诉服务端去获取消息。当检测到服务端返回的消息后,通知客户端去获取消息。(监视器+传声筒)

java netty 死链接 netty 连接池原理_java netty 死链接

EventLoopGroup 是用来生成 EventLoop 的,一个 EventLoopGroup 中包含了多个 EventLoop 对象。EventLoopGroup 要做的就是创建一个新的 Channel,并且给它分配一个 EventLoop。

3、ChannelHandler,ChannelPipeline 和 ChannelHandlerContext

ChannelHandler 是事件的处理者,开发者可以在其上添加一些业务代码,例如数据转换,逻辑运算等。ChannelInBoundHandler(入站事件处理器)和ChannelOutBoundHandler(出站事件处理器)分别处理入站和出站事件。

HannelPipeline 为 ChannelHandler 链提供了容器。当Channel 被创建的时候,会被 Netty 自动分配到 ChannelPipeline 上框架自动分配到 ChannelPipeline 上。ChannelPipeline 保证 ChannelHandler 按照一定顺序处理事件,当事件触发以后,会将数据通过 ChannelPipeline 按照一定的顺序通过 ChannelHandler。

说白了,ChannelPipeline 是负责“排队”的。这里的“排队”是处理事件的顺序,也可以添加或者删除 ChannelHandler,管理整个队列。

ChannelHandlerContext表示ChannelPipeline 和 ChannelHandler的关联。当ChannelHandler 添加到 ChannelPipeline 时,同时会创建 ChannelHandlerContext。

ChannelHandlerContext 的主要功能是管理 ChannelHandler 和 ChannelPipeline 的交互。ChannelHandlerContext 参数贯穿 ChannelPipeline,将信息传递给每个 ChannelHandler,是个合格的“通讯员”。

java netty 死链接 netty 连接池原理_tcp/ip_02

四、Netty的数据容器

Netty 将 ByteBuf 作为数据容器,来存放数据。

1、ByteBuf 工作原理

ByteBuf 由一串字节数组构成。数组中每个字节用来存放信息。

ByteBuf 提供了两个索引,一个用于读取数据,一个用于写入数据。这两个索引通过在字节数组中移动,来定位需要读或者写信息的位置。当从 ByteBuf 读取时,它的 readerIndex(读索引)将会根据读取的字节数递增。同样,当写 ByteBuf 时,它的 writerIndex 也会根据写入的字节数进行递增。

极限的情况是 readerIndex 刚好读到了 writerIndex 写入的地方。如果 readerIndex 超过了 writerIndex 的时候,Netty 会抛出 IndexOutOf-BoundsException 异常。

2、ByteBuf 使用模式

存放缓冲区的不同分为三类:

a、堆缓冲区

ByteBuf 将数据存储在 JVM 的堆中,通过数组实现,可以做到快速分配。由于在堆上被 JVM 管理,在不被使用时可以快速释放。可以通过 ByteBuf.array() 来获取 byte[] 数据。

b、直接缓冲区

在 JVM 的堆之外直接分配内存,用来存储数据。其不占用堆空间,使用时需要考虑内存容量。

C、复合缓冲区

顾名思义就是将上述两类缓冲区聚合在一起。Netty 提供了一个 CompsiteByteBuf,可以将堆缓冲区和直接缓冲区的数据放在一起,让使用更加方便。

3、ByteBuf 的分配

有两种ByteBufAllocator 的实现,他们分别是:

a、PooledByteBufAllocator,实现了 ByteBuf 的对象的池化,提高性能减少内存碎片。

b、Unpooled-ByteBufAllocator,没有实现对象的池化,每次会生成新的对象实例。

五、Bootstrap 和ServerBootstrap

核心组件都是通过 Bootstrap或者ServerBootstrap来配置并且引导启动的。

ServerBootstrap(服务端引导)绑定一个端口,用来监听客户端的连接请求。而 Bootstrap(客户端引导)只要知道服务端 IP 和 Port 建立连接就可以了。

Bootstrap(客户端引导)需要一个 EventLoopGroup,但是 ServerBootstrap(服务端引导)则需要两个 EventLoopGroup。