netty源码分析(五):pipline

165 阅读3分钟

开始文章前,还是要粘贴netty启动代码

/**
 * @author 彭青松
 */
public class MyServer {

    public static void main(String[] args) {
        // 1.构建 bossGroup 和 workGroup
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        bossGroup.setIoRatio(20);
        EventLoopGroup workGroup = new NioEventLoopGroup(1);

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 2.设置group
        serverBootstrap.group(bossGroup, workGroup);
        // 3.设置服务端option和attr
        serverBootstrap.option(ChannelOption.SO_BACKLOG, 1);

        serverBootstrap.attr(AttributeKey.valueOf("name"),"彭青松");

        // 4.设置服务端的channel
        serverBootstrap.channel(NioServerSocketChannel.class);
        // 5.设置服务端的handler
        serverBootstrap.handler(new MyServerHandler());
        // 6.设置客户端的handler
        serverBootstrap.childHandler(new ChannelInitializer() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                // 7.客户端channel初始化的时候,调用这里
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast(new MyTcpClinetHandler());
            }
        });

        try {
            // 8.服务绑定端口,启动
            ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

一.pipline概述

pipline是管道的意思,每一个channel建立后,内部都会创建一个pipline,pipline里面会把handler封装成一个双向链表,当channel有读写事件时候,数据就在这个pipline中传递,进入每一个handle,进行处理,如下如:

image.png

二.pipline初始化

pipline是在socketcChannel创建的时候一同创建出来的,我们可以看下NioSocketChannel和NioServerSocketChannel的集成体系

image.png

从图中可以看出,他俩都是集成AbstractNioChannel image.png 在最后创建pipline

pipeline = newChannelPipeline();
protected DefaultChannelPipeline newChannelPipeline() {
    return new DefaultChannelPipeline(this);
}

继续进入

protected DefaultChannelPipeline(Channel channel) {
    //1.pipline中保存channel
    this.channel = ObjectUtil.checkNotNull(channel, "channel");
    succeededFuture = new SucceededChannelFuture(channel, null);
    voidPromise =  new VoidChannelPromise(channel, true);
    //2.创建尾节点
    tail = new TailContext(this);
    //3.创建头节点
    head = new HeadContext(this);
    //4.构建双向链表
    head.next = tail;
    tail.prev = head;
}

重点是创建头/尾的handlerContext

image.png 从集成图中可以看到这头尾handlercontext都是继承

AbstractChannelHandlerContext

下面我们分析

2.1 TailContext创建

image.png

创建的时候,会区分这个handlerContext是inboud还是outbound, 在消息从不同的入口进来时候,会触发inboud还是outboud的handler来处理数据

2.2 HeadContext创建

HeadContext和TailContext有共同的父类,他们创建的逻辑是一样,只是名字不同,HeadContext 是outbound事件

image.png

2.3 构建handlerContext双向链表

image.png

比较简单,就是头结点指向尾结点,尾结点指向头结点

三.添加handler

在文章开头的netty启动代码中,可以看到pipeline.addLast(new MyTcpClinetHandler()),这里就是添加handler,进入代码

image.png

image.png

而addLast0()方法也很简单,就是找到tail前面的一个节点,将需要添加的节点放在这里,并重新构建双向链表

private void addLast0(AbstractChannelHandlerContext newCtx) {
    AbstractChannelHandlerContext prev = tail.prev;
    newCtx.prev = prev;
    newCtx.next = tail;
    prev.next = newCtx;
    tail.prev = newCtx;
}

四.删除handler

public final ChannelPipeline remove(ChannelHandler handler) {
    remove(getContextOrDie(handler));
    return this;
}

image.png 这里逻辑也比较简单,逻辑是

  • 1.从head节点开始,遍历找到这个节点
  • 2.删除该节点,并重构pipline的双向链表
  • 3.触发handlerRemove()方法

五.inboound事件传播

客户端的读事件就是inboud事件,会从head节点一次往下找到类型是inboud的handlerContext,进行处理

当客户端的channel创建后,客户端写入数据后,服务端这个selector会监测到这个读事件

image.png

image.png

public final ChannelPipeline fireChannelRead(Object msg) {
   // 这里是从head节点开始执行ChannelRead方法
   AbstractChannelHandlerContext.invokeChannelRead(head, msg);
    return this;
}

接下来就是当前节点处理后, 就会继续找下一个inboud的handlerContext,进行触发,如果管道中这个消息没有被我们业务代码处理,最后会到tailHandlerContext中,这里只是打印日志,大家可以自行调试一下代码

image.png

六.outboud事件传播

如果服务端调用ctx.pipeline().write(msg)方法,就会从pipline的tail节点开始,依次触发write方法

ctx.pipeline().write(msg);

image.png outbound和inboud的流程差不多,outboud是从tail节点开始,依次去查找outboud类型的handlerContext,然后触发write,走到HeadContext的时候,由unsafe类调用基层api,将消息发送出去

七.异常传播

异常传播,不区分inboud和outboud事件,直接往下传播,如果都没有被处理,会到tailContext中,打印日志