全文围绕下图,Netty-Channel的简化版架构体系图展开,从顶层Channel接口开始入手,往下递进,闲言少叙,直接开撸
概述: 从图中可以看到,从顶级接口Channel开始,在接口中定义了一套方法当作规范,紧接着的是来两个抽象的接口实现类,在这个抽象类中对接口中的方法,进行了部分实现,然后开始根据不同的功能分支,分成服务端的Channel和客户端的Channel
回顾 Channel的分类根据服务端和客户端,Channel可以分成两类(这两大类的分支见上图):
服务端: NioServerSocketChannel
客户端: NioSocketChannel
什么是Channel?channel是一个管道,用于连接字节缓冲区Buf和另一端的实体,这个实例可以是Socket,也可以是File, 在Nio网络编程模型中, 服务端和客户端进行IO数据交互(得到彼此推送的信息)的媒介就是Channel
Netty对Jdk原生的ServerSocketChannel进行了封装和增强封装成了NioXXXChannel, 相对于原生的JdkChannel, Netty的Channel增加了如下的组件
id 标识唯一身份信息
可能存在的parent Channel
管道 pepiline
用于数据读写的unsafe内部类
关联上相伴终生的NioEventLoop
本篇博客,会追溯上图中的体系关系,找出NioXXXChannel的相对于jdk原生channel在哪里添加的上面的新组件
源码开始-Channel现在来到上图的Channel部分, 他是一个接口, netty用它规定了一个Channel是该具有的功能,在它的文档对Channel的是什么,以及对各个组件进行了描述
阐述了channel是什么,有啥用
Channel通过ChannelPipeline中的多个Handler处理器,Channel使用它处理IO数据
Channel中的所有Io操作都是异步的,一经调用就马上返回,于是Netty基于Jdk原生的Future进行了封装, ChannelFuture, 读写操作会返回这个对象,实现自动通知IO操作已完成
Channel是可以有parent的, 如下
// 创建客户端channel时,会把服务端的Channel设置成自己的parent // 于是就像下面: 服务端的channel = 客户端的channel.parent(); 服务的channel.parent()==null;此外,Channel还定义了大量的抽象方法, 如下:
/** * todo 返回一个仅供内部使用的unsafe对象, Chanel上 IO数据的读写都是借助这个类完成的 */ Unsafe unsafe(); // 返回Channel的管道 ChannelPipeline pipeline(); ByteBufAllocator alloc(); @Override // todo 进入第一个实现 , 读取Channel中的 IO数据 Channel read(); // 返回Channel id ChannelId id(); // todo 返回channel所注册的 eventLoop EventLoop eventLoop(); // 返回当前Channel的父channel Channel parent(); // todo 描述了 关于channel的 一些列配置信息 ChannelConfig config(); // 检查channel是否开启 boolean isOpen(); // 检查channel是否注册 boolean isRegistered(); // todo 什么是active 他说的是channel状态, 什么状态呢? 当前channel 若和Selector正常的通信就说明 active boolean isActive(); // 返回channel的元数据 ChannelMetadata metadata(); // 服务器的ip地址 SocketAddress localAddress(); // remoteAddress 客户端的ip地址 SocketAddress remoteAddress(); ChannelFuture closeFuture(); boolean isWritable(); long bytesBeforeUnwritable(); long bytesBeforeWritable(); @Override Channel flush(); Channel重要的内部接口 unsafeNetty中,真正帮助Channel完成IO读写操作的是它的内部类unsafe, 源码如下, 很多重要的功能在这个接口中定义, 下面列举的常用的方法
interface Unsafe { // 把channel注册进EventLoop void register(EventLoop eventLoop, ChannelPromise promise); // todo 给channel绑定一个 adress, void bind(SocketAddress localAddress, ChannelPromise promise); // 把channel注册进Selector void deregister(ChannelPromise promise); // 从channel中读取IO数据 void beginRead(); // 往channe写入数据 void write(Object msg, ChannelPromise promise); ... ... AbstractChanel接着往下看,下面来到Channel接口的直接实现类,AbstractChannel 他是个抽象类, AbstractChannel重写部分Channel接口预定义的方法, 它的抽象内部类AbstractUnsafe实现了Channel的内部接口unsafe
我们现在是从上往下看,但是当我们创建对象使用的时候其实是使用的特化的对象,创建特化的对象就难免会调层层往上调用父类的构造方法, 所以我们看看AbstractChannel的构造方法干了什么活? 源码如下:
protected AbstractChannel(Channel parent) { this.parent = parent; // todo channelId 代表Chanel唯一的身份标志 id = newId(); // todo 创建一个unsafe对象 unsafe = newUnsafe(); // todo 在这里初始化了每一个channel都会有的pipeline组件 pipeline = newChannelPipeline(); }