2024-04-04
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1369203563

回答

Netty 重新设计一个 Channel 主要是为了解决 Java NIO 中 Channel 的局限性,并提供更加高效、灵活且功能更多的 Channel。

  1. Java NIO 的 Channel 功能太少,不满足 Netty 的要求。
  2. Java NIO 的 Channel 和其他组件使用起来相对比较复杂,对初学者来说使用起来较为困难。而 Netty 提供的 Channel 设计的更加抽象,提供了更易用的 API。
  3. Java NIO 的 ServerSocketChannel 和 SocketChannel 是一个 SPI 接口,具体的实现由虚拟厂商来实现,直接通过原生 ServerSocketChannel 和 SocketChannel 来实现及满足 Netty 的要求,其工作量不亚于重新开发一个。
  4. Netty 的 Channel 需要符合 Netty 的整体架构设计,他需要和 Netty 的整体架构耦合在一起,比如 IO 模型、基于元数据描述配置化的TCP参数等等,原生的 Channel 都不支持。
  5. 自定义的 Channel,灵活性更高,功能更加全面。

扩展

Channel 是 Netty 的核心概念之一,它是 Netty 网络 IO 操作的抽象,即 Netty 网络通信的主体,由它来负责对端进行网络通信、注册、数据操作等一切 IO 相关的操作,其主要功能包括:

  1. 网络 IO 的读写
  2. 客户端发起连接
  3. 关闭连接
  4. 网络连接的相关参数
  5. 绑定端口
  6. Netty 框架相关操作,如获取 Channel 相关联的 EventLoop、pipeline 等。

Channel 原理

Channel 的核心原理如下图:

  1. 客户端与服务端成功建立连接后,服务端会为该连接创建一个 Channel。
  2. Channel 从 EventLoopGroup 中获取一个 EventLoop,Channel 注册到该 EventLoop 中,从此 Channel 就与该 EventLoop 绑定在一起了,在 Channel 整个生命周期内都只会与该 EventLoop 绑定在一起。
  3. 客户端发起的 IO 操作,在 Channel 中都将产生相对应的 Event,触发与该 Channel 绑定的 EventLoop 进行处理
  4. 如果是读写事件,执行线程调度 ChannelPipeline 来处理业务逻辑。ChannelPipeline 只负责 Handler 的编排,真正执行任务的是各个具体的 ChannelHandler。

Channel 的状态转换

Channel 从创建到消亡,他有四种状态,分别是:

  1. 打开状态(Open)
    1. Channel 处于打开状态时,表示它已经被创建,但尚未绑定到任何地址或连接到远端服务器。
  2. 活动状态(Active)
    1. Channel 处于活动状态时,表示它已经成功绑定到本地地址或连接到远端服务器。
    2. 这个时候可以调用 writeAndFlush() 向对方发送数据了。
  3. 非活动状态(Inactive)
    1. Channel 处于非活动状态时,表示它已经处于活动状态,但连接已经断开或由于其他原因不可用。
    2. 当连接被关闭或出现错误时,Channel 会进入非活动状态。
    3. 无法进行读取或写入操作,但可重新激活 Channel。
  4. 关闭状态(Closed):
    1. Channel 处于关闭状态时,表示它已经完全关闭,无法再进行任何操作。

状态流转如下:

Netty 提供了四个方法来判断 Channel 的状态:

  • isOpen():检查 Channel 是否为 open 状态。
  • isRegistered():检查 Channel 是否为 registered 状态。
  • isActive():检查 Channel 是否为 active 状态。
  • isWritable():这个方法有误导性,它并不是判断当前 Channel 是否可写,实际上它是用来检测当前 Channel 的写操作是否可以立刻被 IO 线程处理,当该方法返回 false 时,任何写请求都会被阻塞,知道 I/O 线程有能力能处理这些请求。

各个状态以及他们对应的操作如下表格:

状态 isOpen() isActive() close() writeAndFlush() 读操作 写操作
打开(Open) true false true false true true
活动(Active) true true true true true true
非活动(Inactive) true false true false false false
关闭(Closed) false false false false false false

Channel 的 API

Channel 常用的 API 非常多,如下图(部分):

方法虽然多,但是总体大致分为如下几类:

类 getter API

这里方法主要用于获取 Channel 相关的属性,如绑定地址,相关配置等等

  • SocketAddress localAddress():返回与 Channel 绑定的本地地址
  • SocketAddress remoteAddress():返回与 Channel 绑定的远端地址
  • ChannelConfig config():返回一个 ChannelConfig 对象,通过这个对象可以配置Channel相关的参数
  • ChannelMetadata metadata():返回一个 ChannelMetadata 对象,ChannelMetadata 可以查询 Channel 实现是否支持某种操作,目前它还只要一个方法 hasDisconnect(),用来判断 Channel 实现是否支持 disconnect() 操作。
  • Channel parent():返回 Channel 的 parent Channel。SocketChannel 返回的是相对一个的 ServerSocketChannel,而 ServerSocketChannel 则返回 null。为什么 SocketChannel 返回的是 ServerSocketChannel 呢?因为所有的 SocketChannel (客户端发起连接)都是由 ServerSocketChannel 接受连接而创建的,所以 SocketChannel 的 parent() 返回的就是对应的 ServerSocketChannel 。
  • EventLoop eventLoop():返回 Channel 注册的 EventLoop。
  • ChannelPipeline pipeline():返回与 Channel 关联的 ChannelPipeline。
  • ByteBufAllocator alloc():返回与 Channel 关联的 ByteBufAllocator 对象。
  • Unsafe unsafe():返回 Channel 的 Unsafe 对象。Unsafe 是 Channel 的内部类,只供 Channel 内部使用。

Future 相关 API

Netty 中的所有 IO 操作都是异步的,这就意味着任何的 IO 调用都将立刻返回,但是并不能保证所有的操作都在调用结束后就完成了,而且我们也不知道 IO 操作执行的结果。Netty 在完成 IO 调用后会返回一个 Future 对象,该对象就是 Channel 异步 IO 的结果。Channel 提供了我们操作这些 Future 的方法:

  • ChannelFuture closeFuture():当 Channel 关闭时返回一个 ChannelFuture,我们可以通过该方法来来对 Channel 关闭后做一些处理。
  • ChannelPromise voidPromise():返回一个 ChannelPromise 实例对象。
  • ChannelPromise newPromise():返回一个 ChannelPromise。
  • ChannelProgressivePromise newProgressivePromise():返回一个新的 ChannelProgressivePromise 实例对象。
  • ChannelFuture newSucceededFuture():创建一个新的 ChannelFuture,并将其标注为 succeed 。
  • ChannelFuture newFailedFuture(Throwable cause):创建一个新的 ChannelFuture,并将其标注为 failed。

ChannelFutureChannelPromise 是 Netty 提供的两个特殊 Future,利用他们我们能够在 Netty 完成一些异步操作的处理。

判断状态 API

Channel 提供了四个 isXxx 方法用来判断 Channel 的状态:

  • boolean isOpen():判断 Channel 是否 opened
  • boolean isRegistered():判断 Channel 是否 registered
  • boolean isActive():判断 Channel 是否 active
  • boolean isWritable():判断 Channel 是否可以立刻处理 IO 事件

事件触发类方法

这些方法都会触发 IO 事件,他们都会通过 ChannelPipeline 传播然后被 ChannelHandler 处理。

  • ChannelFuture bind(SocketAddress localAddress):服务端绑定本地端口,开始监听客户端的连接请求。
  • ChannelFuture connect(SocketAddress remoteAddress):客户端向服务端发起连接请求。
  • ChannelFuture disconnect():断开连接,但是需要注意的是该方法不会释放资源,它还可以再次通过 connect() 再次与服务端建立连接。
  • ChannelFuture close():关闭通道,释放资源。
  • Channel read():读取通道
  • ChannelFuture write(Object msg):向 Channel 中写入数据,该方法并不会将数据真实地写入通道,它只将数据写入到了通断缓存区,我们需要调用 flush() 将缓存区的数据刷入到 Channel 中。
  • Channel flush():将数据刷写到 Channel 中。
  • ChannelFuture writeAndFlush(Object msg):相当于调用了 write()flush()
阅读全文