Netty之Pipeline的原理和事件传播机制
文章目录
一、包含知识点
- ChannelPipeline初始化流程
- ChannelPipeline初始化流程
- ChannelInitializer添加
- 自定义ChannelHandler添加入队列
- Pipeline事件传播机制
二、 Channel和Pipeline之间关系
本篇文章主要讲解Netty中Pipeline相关的知识点 , 而pipeline和Channel有着紧密的联系, 数据的读取、写入都需要经过Channel,而Pipeline被绑定到了Channel, 那么从哪里可以知道Channel和Pipeline之间有关系呢 ?
常见的Channel有NioServerSocketChannel、NioSocketChannel, 这里我们看下NioServerSocketChannel类继承关系, 从类继承关系 NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel 进行查找, 在AbstractChannel抽象类中我们找到了pipeline变量, 这说明了Channel和Pipeline是有相互关系的
//AbstractChannel
private final DefaultChannelPipeline pipeline;
三、ChannelPipeline初始化流程
3.1 Channel初始化流程
在第二节中我们知道Channel和Pipeline之间存在联系, 那么Pipeline的初始化是否和Channel初始化有关系呢?带着这个疑问, 先查看抽象类AbstractChannel, 在构造方法中看到下面的代码, pipeline是在构造函数中创建的, 而构造函数在初始化的时候才会调用, 也就是Channel初始化的时候会同时创建pipeline对象。
//AbstractChannel
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
protected AbstractChannel(Channel parent, ChannelId id) {
this.parent = parent;
this.id = id;
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
在上篇 Netty 核心原理之运行机制 文章中我们提到了Channel创建方式,服务启动(bind、connect)的时候会执行initAndRegister()方法, 该方法会执行channelFactory.newChannel()创建具体的Channel, 这里以NioServerSocketChannel为例说明
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
/**
* Channel对象的创建, 来自下面语句
* channel(NioServerSocketChannel.class)
*/
channel = channelFactory.newChannel(); // 创建Channel对象, 这里是 NioServerSocketChannel
init(channel); // 对Channel对象进行初始化
} catch (Throwable t) {
//... 省略部分其它代码
}
//... 省略部分其它代码
return regFuture;
}
Channel构造方法初始化调用流程如下, 从构造函数初始化流程在AbstractChannel抽象类中找到了pipeline创建逻辑, 和本小节开始时分析是一致的。
//NioServerSocketChannel
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
// AbstractNioMessageChannel
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
// AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
//...省略其它部分代码
}
//AbstractChannel
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
3.2 Pipeline实例化
pipeline实例化是调用内部方法newChannelPipeline创建的
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
DefaultChannelPipeline创建的时候做了什么事情呢? 看下下面的代码
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
可以看到, 入参channel赋值给了this.channel, 同时创建了两个字段tail、head,这两个字段维护了以AbstractChannelHandlerContext为节点的双向链表, 分别表示队尾、队头,这个双向链表是Netty实现Pipeline机制的关键, 下面分别看下HeadContext、TailContext类结构图
从类结构图可以看出, HeadContext、TailContext主要相似和区别是
- 差异
- HeadContext实现了ChannelOutboundHandler(虽然也实现了ChannelInboundHandler)
- TailContext实现了ChannelInboundHandler
- 相似
- 都实现了ChannelHandler、ChannelHandlerContext, 可以说head、tail既是一个ChannelHandler, 也是ChannelHandlerContext
从上面类继承关系我们知道HeadContext、TailContext的差异是ChannelOutboundHandler、ChannelInboundHandler, 那从哪里可以直观的这种区别呢 ? 我们看下HeadContext、TailContext构造方法
//HeadContext
HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, HEAD_NAME, false, true);
unsafe = pipeline.channel().unsafe();
setAddComplete();
}
//TailContext
TailContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, TAIL_NAME, true, false);
setAddComplete();
}
从构造方法中, 主要差别是super调用时入参的区别
- name的区别, 这点事显然的为了区别队列的头和尾
- inbound、outbound的区别
- head: inbound – false ,outbound – true
- tail: inbound – true ,outbound – false
3.3 Pipeline实例化后图示
四、ChannelInitializer添加
4.1 init添加自定义handler、childHandler
首先我们看下ChannelInitializer的使用代码示例
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new new LengthFieldBasedFrameDecoder())
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//对象参数类型编码器
pipeline.