Netty 中的 Channel 可以看成网络编程中的 Socket,其提供了一系列 IO 操作的 API,比如 read、write、bind、connect 等,大大降低了直接使用 Socket 类的复杂性。同时也包含了 Netty 框架相关的一些功能,包括获取 Channel 的 EventLoop,获取缓冲区分配器 ByteBufAllocator 和 pipeline 等。
Channel 类体系的设计与其实现功能密不可分,父类中实现的是子类共同的功能。在多层次的抽象类中,每一个层次的抽象类负责实现一种功能。
从上面的继承关系可以看出,NioSocketChannel 和 NioServerSocketChannel 分别对应客户端和服务端的 Channel,两者的直接父类不一致,因此对外提供的功能也是不相同的。比如当发生 read 事件时,NioServerSocketChannel 的主要逻辑就是建立新的连接,而 NioSocketChannel 则是读取传输的字节进行业务处理。
Channel 定义了 核心方法
public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable<Channel> {
// 返回此Channel的全局唯一标识符
ChannelId id();
// 返回此Channel被注册到的EventLoop
EventLoop eventLoop();
// 返回此Channel的父Channel,如果是ServerSocketChannel实例则返回null,而SocketChannel实例则返回创建其对应的ServerSocketChannel
Channel parent();
// 返回该通道的配置参数
ChannelConfig config();
// 如果通道是打开的并且稍后可能会激活,则返回true
boolean isOpen();
// 返回此Channel是否已经注册到EventLoop
boolean isRegistered();
// 返回此Channel是否处于激活状态
boolean isActive();
// 返回此channel的元数据
ChannelMetadata metadata();
// 返回此channel绑定到的本地地址。
SocketAddress localAddress();
// 返回连接到此channel的远程地址。
SocketAddress remoteAddress();
// 通道的关闭凭证,当该通道关闭时将通知它。可以同步阻塞,也可以添加执行器异步去处理
ChannelFuture closeFuture();
// 是否可写,如果通道的写缓冲区未满,即返回true,表示写操作可以立即 操作缓冲区,然后返回。
boolean isWritable();
long bytesBeforeUnwritable();
long bytesBeforeWritable();
// 返回 Unsafe
Unsafe unsafe();
// 返回 管道
ChannelPipeline pipeline();
// 返回 字节缓冲区分配器
ByteBufAllocator alloc();
@Override
Channel read();
@Override
Channel flush();
interface Unsafe {
// 略...
}
}
Unsafe ——Channel 的辅助接口
此 Unsafe 非 java中的Unsafe,它可以看作是 Channel 的辅助接口,它不能被用户代码直接调用!实际的网络IO操作基本都是由 Unsafe功能类负责实现的。
其中 io.netty.channel.AbstractChannel.AbstractUnsafe
实现了register、bind、close等方法。其还有一个抽象类io.netty.channel.nio.AbstractNioChannel.AbstractNioUnsafe
,它包含了两个常用的实现类:
io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe
,通过以下初始化源码分析我们发现NioServerSocketChannel初始化时创建的unsafe是NioMessageUnsafe。
io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe
下面就以 NioServerSocketChannel 为例,带大家了解下该类的初始化过程,整体流程如下:
在上面的服务端启动过程中,ServerBootstrap 调用 channel() 方法并传入 NioServerSocketChannel,其底层代码逻辑为:
/**
* 用于从中创建通道实例的类。
*/
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
// ReflectiveChannelFactory 构建方法
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
整体逻辑很简单,通过传入的 Class 对象指定一个 Channel 反射工厂,后续调用工厂方法获取指定类型的 Channel 对象。
ChannelFactory 的整体逻辑就是通过反射的方式新建 Channel 对象,而 Channel 对象的类型则是在启动引导类中通过 channel() 方法进行指定的。在实例化 Channel 的过程中,会对其内部的一些属性进行初始化,而对这些属性的了解,可以使我们对 Netty 中各个组件的作用范围有一个更加清晰的理解,下面看下 NioServerSocketChannel 的构造函数源码