Channel和Unsafe

本文详细介绍了Netty框架中Channel的设计理念与实现细节,包括为何重新设计Channel接口、Channel的工作原理及其实现方式。此外,还深入探讨了Unsafe接口的作用及其实现机制。

  提起Channel,会想到JDK的NIO类库的重要组成部分:java.nio.SocketChanneljava.nio.ServerSocketChannel,用于非阻塞的I/O操作。
  类似于NIO的Channel,Netty提供自己的Channel及其子类实现,用于异步I/O操作和其他相关的操作。
  Unsafe是内部接口,聚合在Channel中协助进行网络读写相关的操作,因为它的设计初衷就是Channel的内部辅助类,不应该被Netty框架的上层使用者调用。

Channel工作原理

  Channel是Netty抽象出来的网络I/O读写相关的接口,为什么不使用JDK NIO原生的Channel而要另外炉灶,主要原因:
  1)JDK的SocketChannel和ServerSocketChannel没有统一的Channel接口供业务开发者使用,对于用户而言,没有统一的操作视图,使用起来并不方便;
  2)JDK的SocketChannel和ServerSocketChannel的主要职责就是网络I/O操作,由于他们是SPI类接口,由具体的虚拟机厂家来提供,所以通过继承SPI功能类来扩展其功能的难度很大;直接实现ServerSocketChannel和SocketChannel抽象类,其工作量和重新开发一个新的Channel功能类是差不多的;
  3)Netty的Channel需要能够跟Netty的整体架构融合在一起,例如I/O模型、基于ChannelPipeline的定制模型,以及基于元数据描述配置化的TCP参数等,这些JDK的ScoketChannel和ServerSocketChannel都没有提供,需要重新封装;
  4)自定义的Channel,功能实现更加灵活。

  基于上述4个原因,Netty重新设计了Channel接口,并且给予了很多不同的实现。它的设计原理比较简单,但是功能却比较繁杂,主要的设计理念:
  1)在Channel接口层,采用Façade模式进行统一封装,将网络I/O操作、网络I/O相关联的其他操作封装起来,统一对外提供;
  2)Channel接口的定义尽量大而全,为SocketChannel和ServerSocketChannel提供统一的视图,由不同子类实现不同的功能,公共功能在抽象父类中实现,最大程度上实现功能和接口的重用;
  3)具体实现采用聚合而非包含的方式,将相关的功能聚合在Channel中,由Channel统一负责配置和调度。

  io.netty.channel.Channel是Netty网络操作抽象类的主要功能:
  1)网络的读、写;
  2)客户端发起连接、主动关闭连接
  3)链路关闭
  4)获取通信双方的网络地址
  5)获取该Channel的EventLoop
  6)获取缓冲区分类器ByteBufAllocator和pipeline等
  7)获取元数据metadata()
  4)parent():对于服务端Channel为空;对于客户端Channel为创建它的ServerSocketChannel
  5)id() 返回ChannelId对象 Channel的唯一标识符:机器MAC地址+当前进程ID+时间毫秒+时间纳秒+32位随机整数+32位自增序列数。

  Channel类图
这里写图片描述 

  AbstractChannel:采用聚合的方式封装各种功能:

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {

    ……

    private final Channel parent;
    private final ChannelId id;
    private final Unsafe unsafe;
    private final DefaultChannelPipeline pipeline;
    private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
    private final CloseFuture closeFuture = new CloseFuture(this);

    private volatile SocketAddress localAddress;
    private volatile SocketAddress remoteAddress;
    private volatile EventLoop eventLoop;
    private volatile boolean registered;

    /** Cache for the string representation of this channel */
    private boolean strValActive;
    private String strVal;

  可以从成员变量的定义可以看出来,聚合了:parent, id, unsafe, pipeline, eventLoop等。

Unsafe

  Unsafe接口实际上是Channel接口的辅助接口,它不应该被用户直接调用。实际的网络I/O读写操作都是由Unsafe接口及其功能类负责实现的。它的含义不是说它的方法是不安全的,而是说它的接口是给框架本身调用的,不要暴露给业务层调用。
这里写图片描述
  Unsafe的最底层实现类采用了模板方法模式,NioMessageUnsafe绑定到了NioServerSocketChannelNioByteUnsafe绑定到NioSocketChannel,最终的IO读写方法实现在NioServerSocketChannelNioByteUnsafe中,调用了Java的ServerSocketChannelSocketChannel来实现。

在 Netty 中,`Channel.register` `Channel.bind` 是两个关键操作,分别用于不同的目的,尽管它们都涉及到网络通信的初始化过程。 ### `Channel.register` 方法的作用 `Channel.register` 方法的主要作用是将一个 `Channel` 注册到一个 `EventLoop` 上,这样该 `Channel` 就可以开始监听 I/O 事件(如读写事件)。在 Netty 的启动流程中,当通过 `ServerBootstrap.bind()` 方法绑定端口时,会创建一个 `Channel` 实例,并调用 `register` 方法将其注册到 `EventLoopGroup` 中的一个 `EventLoop` 上。这一过程通常发生在服务器启动时的初始化阶段 [^2]。 具体来说,`register` 方法会触发 `ChannelPipeline` 中的 `handlerAdded` 方法,确保所有的 `Handler` 都被正确添加并初始化。此外,它还负责将底层的 `SelectableChannel`(例如 `ServerSocketChannel`)注册到 `Selector` 上,但此时并未绑定具体的端口地址。 ### `Channel.bind` 方法的作用 `Channel.bind` 方法则用于将 `Channel` 绑定到一个具体的本地地址(通常是 IP 地址端口号)。对于服务器端的 `Channel` 来说,这一步是必须的,因为只有绑定了端口,才能开始监听客户端的连接请求。在 Netty 中,`bind` 方法通常是在 `register` 之后调用的,以确保 `Channel` 已经注册到了 `EventLoop` 上 [^2]。 在调用 `bind` 方法时,Netty 会通过底层的 `Unsafe` 对象执行实际的绑定操作。对于 `ServerSocketChannel` 而言,这一步会调用 `ServerSocketChannel.bind(SocketAddress)` 方法,将通道绑定到指定的地址,并设置连接队列的大小(backlog)。绑定成功后,`Channel` 就可以开始监听新的连接请求,并将这些请求传递给 `ChannelPipeline` 进行处理。 ### 总结区别 - `register` 是将 `Channel` 注册到 `EventLoop` 上,使其能够处理 I/O 事件。 - `bind` 是将 `Channel` 绑定到具体的本地地址上,使其能够监听或发送数据。 这两步操作通常是顺序执行的,先注册 `Channel`,再绑定地址,以确保网络通信的正常启动。 ```java // 示例代码:ServerBootstrap 的 bind 方法内部调用 register bind ChannelFuture future = bootstrap.bind(new InetSocketAddress("localhost", 8080)); future.sync(); // 等待绑定完成 ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值