Netty之Pipeline的原理和事件传播机制

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、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实例化后图示

在这里插入图片描述

图三、DefaultChannelPipeline初始化时队列结构

四、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.
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值