Netty的Channel

Netty的Channel教程

在了解 NIO 的 Channel 的时候,我们知道,它是用于非阻塞 I/O 的操作。类似于 NIO 的 Channel,Netty 提供了自己的 Channel 和其子类实现,用于异步 I/O 操作和其他相关的操作。它聚合了一组功能。

它的里面包含了网络的读、写,客户端发起连接,主动关闭链路,获取通信双方的网络地址等等功能。它里面也包含了 Netty 框架里面的一些东西,获取该 Channel 的 EventLoop,获取缓冲分配器 ByteBufAllocator 和 pipeline 等等。

Netty的Channel工作原理

在 JDK 中会有自带的 SocketChannel 和 ServerSocketChannel,它们各自实现自己的功能,对使用者而言没有统一的操作视图,使用比较繁琐和复杂,对 Netty 的框架的整体融合也不是很好,所以 Netty 就自定义了 Channel ,Netty 的主要设计理念如下:

  1. 在 Channel 接口层,采用 Facade 模式进行统一封装,将网络 I/O 操作、网络 I/O 相关的其他操作封装起来,统一对外提供。
  2. Channel 接口的定义尽量全而大,为 SockerChannel 和 ServerSocketChannel 提供统一的视图,由不同的子类实现不同的功能,公共功能在抽象父类中实现,最大程度地实现功能和接口的重用。
  3. 具体实现采用聚合而非包含的方式,将相关的功能类聚合在 Channel 中,由 Channel 统一负责分配和调度,功能实现更加灵活。

Netty的Channel功能

Channel 是一个接口,它里面包含的方法有:

18 channel 接口方法.png

  1. Channel read(); 从当前的 Channel 中读取数据到第一个 inbound 缓冲区中,如果数据被成功读取,触发 ChannelInboundHandler.channelRead(ChannelHandlerContext, Object) 事件。读取操作 API 调用完成之后,紧接着会触发 ChannelInboundHandler.channelReadComplete (ChannelHandlerContext) 事件,这样业务的 ChannelHandler 可以决定是否需要继续读取数据。如果已经有读操作请求被挂起,则后续的读操作会被忽略。
  2. ChannelFuture write(Object msg); 在它的实现接口 ChannelOutboundInvoker 存在该方法,它将当前的 msg 通过 ChannelPipeline 写入到目标 Channel 中。这边需要关注的是 write 操作只是将消息存入到消息发送环形数组中,没有真正的发送消息,只有调用了 flush 操作才会将数据写入到 Channel 中,发送给对方。
  3. ChannelFuture write(Object msg, ChannelPromise promise); 在它的实现接口 ChannelOutboundInvoker 存在该方法,它的功能与 write(Object msg) 相同,只不过携带了 ChannelPromise 参数用来负责接收写入操作的结果。
  4. ChannelFuture writeAndFlush(Object msg, ChannelPromise promise); 在它的实现接口 ChannelOutboundInvoker 存在该方法,它的功能和 3 差不多,但是它会将消息写入 Channel 中发送,相当于单独调用了 write 和 flush 操作的组合。
  5. Channel flush(); 在它的实现接口 ChannelOutboundInvoker 存在该方法。将之前写入到发送环形数组中的消息全部写入到目标 Channel 中,发送给通信对方。
  6. ChannelFuture close(ChannelPromise promise); 在它的实现接口 ChannelOutboundInvoker 存在该方法。主动关闭当前连接,通过 ChannelPromise 设置操作结果并进行结果通知,无论操作是否成功,都可以通过 ChannelPromise 获取操作结果。该操作会级联触发 ChannelPipeline 中所有 ChannelHandler 的 ChannelOutboundHandler.close(ChannelHandlerContext, ChannelPromise) 事件。
  7. ChannelFuture disconnect(ChannelPromise promise); 在它的实现接口 ChannelOutboundInvoker 存在该方法。请求断开与远程通信对端端连接并使用 ChannelPromise 来获取操作结果的通知消息。该方法会级联触发 ChannelOutboundHandler.disconnect(ChannelHandlerContext, ChannelPromise) 事件。
  8. ChannelFuture connect(SocketAddress remoteAddress);在它的实现接口 ChannelOutboundInvoker 存在该方法。客户端使用指定的服务端地址 remoteAddress 发起连接请求,如果连接因为应答超时而失败,ChannelFuture 中的操作结果就是 ConnectTimeoutException 异常;如果连接被拒绝,操作结果为 ConnectException。该方法会级联触发ChannelOutboundHandler.connect(ChannelHandlerContext, SocketAddress, SocketAddress,ChannelPromise) 事件。
  9. ChannelFuture bind(SocketAddress localAddress); 在它的实现接口 ChannelOutboundInvoker 存在该方法。绑定指定的本地 Socket 地址 localAddress,该方法会级联触发 ChannelOutboundHandler.bind(ChannelHandlerContext, SocketAddress, ChannelPromise) 事件。
  10. ChannelConfig config(); 获取当前 Channel 的配置信息。
  11. boolean isOpen(); 判断当前的 Channel 是否已经打开。
  12. boolean isRegistered(); 判断当前的 Channel 是否已经注册到 EventLoop 上。
  13. boolean isActive(); 判断当前的 Channel 是否已经处于激活状态。
  14. ChannelMetadata metadata(); 获取当前 Channel 的元数据描述信息,包括 TCP 参数配置等。
  15. SocketAddress localAddress(); 获取当前 Channel 的本地绑定地址。
  16. SocketAddress remoteAddress(); 获取当前 Channel 的远程 Socket 地址。
  17. EventLoop eventLoop(); Channel 需要注册到 EventLoop 的多路复用器上,用于处理 I/O 事件,通过 eventLoop() 方法可以获取到 Channel 注册的 EventLoop。EventLoop 本质上就是处理网络读写事件的 Reactor 线程。在 Netty 中,它不仅用来处理网络事件,也可以用来执行定时任务和用户自定义的 NioTask 等任务。

Netty的Channel关系类图

我们知道,Netty 这边主要的分为 服务器端 Channel 和客户端 Channel。服务器端 Channel 关系图如下:

19 NioServerSocketChannel 关系类图.png

客户端 NioSocketChannel 如下:

20 NioSocketChannel 关系类图.png

Netty的Channel总结

本章我们了解了 Netty 的 Channel 相关信息,粗略的讲解了一下里面的常用函数,了解了 Channel 相关的类关系图。