Netty中ChannelHandlerContext、Channel、ChannelPipeline的write()方法底层调用链的不同

最近在看《Netty实战》这本书,看到书中讲解ChannelHandlerContext、Channel、ChannelPipeline的write()的不同时有点懵逼,没大看懂,遂动手写了一个Demo来验证到底是怎么回事。

服务启动类OneServer:

package com.ccf.netty_learn.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class OneServer {
	
	public static void main(String[] args) throws Exception{
		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		EventLoopGroup workGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workGroup)
			 .channel(NioServerSocketChannel.class)
			 .handler(new LoggingHandler(LogLevel.INFO))
			 .childHandler(new ChannelInitializer() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ChannelPipeline cp = ch.pipeline();
					cp.addLast(new StringDecoder());
					cp.addLast(new OneServerOutHandler());
					cp.addLast(new StringEncoder());
					cp.addLast(new OneServerInHandler());
				}
			});
			b.bind(8888).sync().channel().closeFuture().sync();
		}finally {
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
		}
	}

}

入站Handler:

package com.ccf.netty_learn.server;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class OneServerInHandler extends SimpleChannelInboundHandler{

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
		System.out.println("OneServerInHandler:"+msg);
		ctx.write(Unpooled.copiedBuffer(msg.getBytes()));
		ctx.flush();
//		ctx.channel().write(Unpooled.copiedBuffer(msg.getBytes()));
//		ctx.channel().flush();
	}

}

出站Handler:

package com.ccf.netty_learn.server;

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

public class OneServerOutHandler extends ChannelOutboundHandlerAdapter{
	
	@Override
	public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
		System.out.println("OneServerOutHandler-write()");
		super.write(ctx, msg, promise);
	}

}

客户端启动类OneClient:

package com.ccf.netty_learn.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class OneClient {
	public static void main(String[] args) throws Exception{
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap b = new Bootstrap();
			b.group(group)
			 .channel(NioSocketChannel.class)
			 .handler(new ChannelInitializer() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ChannelPipeline cp = ch.pipeline();
					cp.addLast(new StringDecoder());
					cp.addLast(new StringEncoder());
					cp.addLast(new OneClientHandler());
					
				}
			});
			b.connect("localhost", 8888).sync().channel().closeFuture().sync();
		}finally {
			group.shutdownGracefully();
		}
	}

}

客户端Handler:

package com.ccf.netty_learn.client;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class OneClientHandler extends SimpleChannelInboundHandler{

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
		
		System.out.println("OneClientHandler:"+msg);
		
	}
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		ctx.writeAndFlush(Unpooled.copiedBuffer("Hello".getBytes()));
	}

}

一、当服务端的Handler链的顺序如下,既自定义出站Handler在入站Handler之后时,

					ChannelPipeline cp = ch.pipeline();
					cp.addLast(new StringDecoder());
					cp.addLast(new OneServerInHandler());
					cp.addLast(new OneServerOutHandler());
					cp.addLast(new StringEncoder());

在入站Handler中调用ChannelHandlerContext的write()方法结果如下:

八月 21, 2019 11:24:45 上午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0x95fb994b] REGISTERED
八月 21, 2019 11:24:45 上午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0x95fb994b] BIND: 0.0.0.0/0.0.0.0:8888
八月 21, 2019 11:24:45 上午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0x95fb994b, L:/0:0:0:0:0:0:0:0:8888] ACTIVE
八月 21, 2019 11:25:12 上午 io.netty.handler.logging.LoggingHandler channelRead
信息: [id: 0x95fb994b, L:/0:0:0:0:0:0:0:0:8888] RECEIVED: [id: 0xdaf189ea, L:/127.0.0.1:8888 - R:/127.0.0.1:18270]
OneServerInHandler:Hello

说明并没有调用到自定义的出站Handler中的write()方法。

此时如果在入站Handler中改成调用Channel或者ChannelPipeline的write()方法,如下

		System.out.println("OneServerInHandler:"+msg);
//		ctx.write(Unpooled.copiedBuffer(msg.getBytes()));
//		ctx.flush();
		ctx.channel().write(Unpooled.copiedBuffer(msg.getBytes()));
		ctx.channel().flush();

结果是:

八月 21, 2019 11:28:20 上午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0x111c797e] REGISTERED
八月 21, 2019 11:28:20 上午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0x111c797e] BIND: 0.0.0.0/0.0.0.0:8888
八月 21, 2019 11:28:20 上午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0x111c797e, L:/0:0:0:0:0:0:0:0:8888] ACTIVE
八月 21, 2019 11:28:34 上午 io.netty.handler.logging.LoggingHandler channelRead
信息: [id: 0x111c797e, L:/0:0:0:0:0:0:0:0:8888] RECEIVED: [id: 0xa1c88958, L:/127.0.0.1:8888 - R:/127.0.0.1:18371]
OneServerInHandler:Hello
OneServerOutHandler-write()

通过上面的演示确实能证明ChannelHandlerContext、Channel、ChannelPipeline的write()方法底层调用链是不同的,那么到底是什么不同呢,接下来分别进入它们的源码中如一探究竟。

二、源码探索

1、深入到ChannelHandlerContext的write()方法,直到调用到AbstractChannelHandlerContext的如下方法

    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);
        }
    }

上面方法的第一行AbstractChannelHandlerContext next = findContextOutbound();很明显这里是在查找下一个AbstractChannelHandlerContext,那么我们继续深入看看到底是怎么查找的下一个AbstractChannelHandlerContext

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

这就很明显了,这里是倒退的方式去查询的第一个出站Handler,但是我们这里的自定义出站Handler在入站Handler的后面,因此肯定就不会调用到它的write()方法了。

2、接着深入到Channel的write()方法看看,进入到AbstractChannel,发现其直接调用的ChannelPipeline的write()方法

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

进入DefaultChannelPipeline,发现调用了tail的write(),tail是ChannelPipeline的尾结点,它既是一个入站Handler又是一个Handler,接下去又调用到AbstractChannelHandlerContext中去了,也就是回到了上面的情况中,这就表明从尾结点tail开始往回找到所有的出站Handler直到头结点head去执行它们的所有的write()方法。

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

总结:我们需要理解ChannelPipeline中的Handler链,才能完成理解其中入站出站的调用关系,入站是从head到tail,出站则相反从tail到head,随便提一句,异常是不会管是出站还是入站的都从下一个(next)开始,因此一般来说异常处理Handler都加到ChannelPipeline的tail节点前一个节点即可(即自定义Handler的最后一个),从上面的演示来看ChannelHandlerContext的write()方法是从当前节点的上(prev)一个出站节点开始执行,只经过了部分链路,而Channel和ChannelPipeline的write()方法是从尾结点开始往后执行的,经过了全部链路,这就是区别。

你可能感兴趣的:(netty,netty)