NOTE

2.88 Outbound事件

1. 添加handler以便实验 - OutboundHandlerA、OutboundHandlerC - OutboundHandlerB - NettyServer 1.1. 使用nc连接 1.2. 输出结果 1.3. 解释 可以看出是从尾巴往头部调用我们的handler 2. 从pipeli

Java创建于 更新于 historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. 添加handler以便实验

  • OutboundHandlerA、OutboundHandlerC
package com.zsk.server.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;

/**
 * @description:
 * @author: zsk
 * @create: 2019-12-10 23:08
 **/
public class OutboundHandlerA extends ChannelOutboundHandlerAdapter
{
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
    {
        System.out.println(this.getClass().getSimpleName() + ":" + msg);
        ctx.write(msg, promise);
    }
}
  • OutboundHandlerB
package com.zsk.server.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: zsk
 * @create: 2019-12-10 23:08
 **/
public class OutboundHandlerB extends ChannelOutboundHandlerAdapter
{
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
    {
        System.out.println(this.getClass().getSimpleName() + ":" + msg);
        ctx.write(msg, promise);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception
    {
        ctx.executor().schedule(()->{
            ctx.channel().writshoue("Hello World");
        },3, TimeUnit.SECONDS);
    }
}
  • NettyServer
.childHandler(new ChannelInitializer<SocketChannel>()
{
    @Override
    protected void initChannel(SocketChannel channel) throws Exception
    {
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast(new OutboundHandlerA())
                .addLast(new OutboundHandlerC())
                .addLast(new OutboundHandlerB());
    }
});

1.1. 使用nc连接

nc localhost 8000

1.2. 输出结果

OutboundHandlerB:Hello World
OutboundHandlerC:Hello World
OutboundHandlerA:Hello World

1.3. 解释

可以看出是从尾巴往头部调用我们的handler

2. 从pipeline开始调用

我们在OutboundHandlerB的handlerAdded打上断点,使用nc连接,开始debug调试,首先会进入 AbstractChannel#write

public ChannelFuture write(Object msg) {
    return pipeline.write(msg);
}

数据传入pipeline进行传播

3. 先是进入TailContext

DefaultChannelPipeline#write

public final ChannelFuture write(Object msg) {
    return tail.write(msg);
}

然后通过AbstractChannelHandlerContext进行转发

3.1. 通过AbstractChannelHandlerContext进行转发

AbstractChannelHandlerContext#write

public ChannelFuture write(Object msg) {
    return write(msg, newPromise());
}

public ChannelFuture write(final Object msg, final ChannelPromise promise) {
    if (msg == null) {
        throw new NullPointerException("msg");
    }

    try {
        if (isNotValidPromise(promise, true)) {
            ReferenceCountUtil.release(msg);
            // cancelled
            return promise;
        }
    } catch (RuntimeException e) {
        ReferenceCountUtil.release(msg);
        throw e;
    }
	//这里
    write(msg, false, promise);

    return promise;
}

private void write(Object msg, boolean flush, ChannelPromise promise) {
	//找到下一个节点
    AbstractChannelHandlerContext next = findContextOutbound();
    final Object m = pipeline.touch(msg, next);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        if (flush) {
            next.invokeWriteAndFlush(m, promise);
        } else {
        	//这里
            next.invokeWrite(m, promise);
        }
    } else {
        AbstractWriteTask task;
        if (flush) {
            task = WriteAndFlushTask.newInstance(next, m, promise);
        }  else {
            task = WriteTask.newInstance(next, m, promise);
        }
        safeExecute(executor, task, promise, m);
    }
}

可以看出先是找到下一个节点,然后调用他的write方法

3.1.1. 怎么找到下一个节点的

private AbstractChannelHandlerContext findContextOutbound() {
    AbstractChannelHandlerContext ctx = this;
    do {
        ctx = ctx.prev;
    } while (!ctx.outbound);
    return ctx;
}

无非就是从当前节点往前遍历到上一个outbound tail的上一个outbound是什么呢?就是我们的OutboundHandlerB

4. 然后进入OutboundHandlerB

io.netty.channel.AbstractChannelHandlerContext#invokeWrite

private void invokeWrite(Object msg, ChannelPromise promise) {
    if (invokeHandler()) {
        invokeWrite0(msg, promise);
    } else {
        write(msg, promise);
    }
}


 private void invokeWrite0(Object msg, ChannelPromise promise) {
    try {
    	//这里会进入我们的OutboundHandlerB
        ((ChannelOutboundHandler) handler()).write(this, msg, promise);
    } catch (Throwable t) {
        notifyOutboundHandlerException(t, promise);
    }
}
  • OutboundHandlerB
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
{
    System.out.println(this.getClass().getSimpleName() + ":" + msg);
    ctx.write(msg, promise);
}

进入我们的OutboundHandlerB,打印之后,继续AbstractChannelHandlerContext#write

4.1. 通过AbstractChannelHandlerContext进行转发

5. 接着进入OutboundHandlerC

  • OutboundHandlerC
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
{
    System.out.println(this.getClass().getSimpleName() + ":" + msg);
    ctx.write(msg, promise);
}

进入我们的OutboundHandlerC,打印之后,继续AbstractChannelHandlerContext#write

5.1. 通过AbstractChannelHandlerContext进行转发

6. 再进入OutboundHandlerA

  • OutboundHandlerA
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
{
    System.out.println(this.getClass().getSimpleName() + ":" + msg);
    ctx.write(msg, promise);
}

进入我们的OutboundHandlerA,打印之后,继续AbstractChannelHandlerContext#write

6.1. 通过AbstractChannelHandlerContext进行转发

7. 最后进入HeadContext

  • io.netty.channel.DefaultChannelPipeline.HeadContext#write
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    unsafe.write(msg, promise);
}

public final void write(Object msg, ChannelPromise promise) {
    assertEventLoop();

    ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
    if (outboundBuffer == null) {
        // If the outboundBuffer is null we know the channel was closed and so
        // need to fail the future right away. If it is not null the handling of the rest
        // will be done in flush0()
        // See https://github.com/netty/netty/issues/2362
        safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
        // release message now to prevent resource-leak
        ReferenceCountUtil.release(msg);
        return;
    }

    int size;
    try {
        msg = filterOutboundMessage(msg);
        size = pipeline.estimatorHandle().size(msg);
        if (size < 0) {
            size = 0;
        }
    } catch (Throwable t) {
        safeSetFailure(promise, t);
        ReferenceCountUtil.release(msg);
        return;
    }

    outboundBuffer.addMessage(msg, size, promise);
}

进入我们的HeadContext,继续调用AbstractUnsafe,进行一些收尾的工作