netty channelpipeline 介绍
先给出代码,代码解释在后面。
public class EchoServer1 {
private final int port;
public EchoServer1(int port) {
this.port = port;
}
public void start() throws Exception{
EchoInServerHandler1 inServerHandler1 = new EchoInServerHandler1();
EchoInServerHandler2 inServerHandler2 = new EchoInServerHandler2();
EchoOutServerHandler1 outServerHandler1 = new EchoOutServerHandler1();
EchoOutServerHandler2 outServerHandler2 = new EchoOutServerHandler2();
EventLoopGroup group = new NioEventLoopGroup()
try{
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
System.out.println(socketChannel.pipeline().getClass().getName());
socketChannel.pipeline().addLast(inServerHandler1);
socketChannel.pipeline().addLast(outServerHandler1);
socketChannel.pipeline().addLast(inServerHandler2);
socketChannel.pipeline().addLast(outServerHandler2);
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
}finally {
group.shutdownGracefully().sync();
System.out.println("shutdownGracefully");
}
}
@ChannelHandler.Sharable
public class EchoInServerHandler1 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception {
System.out.println("************************in1 执行************************");
ByteBuf in = (ByteBuf) msg;
System.out.println("in Server1 received: "+in.toString(CharsetUtil.UTF_8));
//ctx.channel().writeAndFlush(Unpooled.wrappedBuffer((in.toString(CharsetUtil.UTF_8)+"-in server1-response").getBytes()));
super.channelRead(ctx,Unpooled.wrappedBuffer(("test11111111111111111").getBytes()));
}
}
@ChannelHandler.Sharable
public class EchoInServerHandler2 extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception {
System.out.println("************************in2 执行************************");
ByteBuf in = (ByteBuf) msg;
System.out.println("in Server2 received: "+in.toString(CharsetUtil.UTF_8));
super.channelRead(ctx,msg);
}
}
@ChannelHandler.Sharable
public class EchoOutServerHandler1 extends ChannelOutboundHandlerAdapter{
@Override
public void write(ChannelHandlerContext ctx , Object msg , ChannelPromise promise) throws Exception {
System.out.println("************************out1 执行************************");
ByteBuf byteMsg = (ByteBuf) msg;
System.out.println("out1 received:"+byteMsg.toString(Charset.forName("utf8")));
super.write(ctx,msg,promise);
}
}
@ChannelHandler.Sharable
public class EchoOutServerHandler2 extends ChannelOutboundHandlerAdapter{
@Override
public void write(ChannelHandlerContext ctx , Object msg , ChannelPromise promise) throws Exception {
System.out.println("************************out2 执行************************");
ByteBuf byteMsg = (ByteBuf) msg;
System.out.println("out2 received:"+byteMsg.toString(Charset.forName("utf8")));
super.write(ctx,msg,promise);
}
}
public static void main(String[] args){
try {
int port = 3333;
System.out.println("cccccccccccccccc");
new EchoServer1(port).start();
}catch (Exception e){
e.printStackTrace();
}
}
}
1.创建channel
创建两个in-channel和两个out-channel,如下图所示:
按照in1,out1,in2,out2的顺序加入到pipeline中。Pipeline在初始化时会自动加入两个默认的channelHandler,分别是in-out(在头部)和in(在尾部)。最终pipeline图如下所示:
绿色箭头是指向前一个out.
因为加入的顺序是in1,out1,in2,out2。所以:
- in1的前一个out是默认out,in1下一个in是in2;
- in2的前一个out是out1,in2下一个in是默认in;
- out1的前一个out是默认out,out1下一个in是in2;
- out2的前一个out是out1,out2下一个in是默认in;
这四点大家先记住,否则下面会混淆。
三个橙色的是pipeline默认加入的,源码如下:
会在pipeline头和尾默认加入。
1.其中head继承了in和out,所有头部有两个(一个in,一个out)。out会与网络连接,把数据写入网络。in会从网络接收数据,并把数据传给我们程序中定义的in1。
2.tail只继承了in,所有尾部只有一个in,这个in不会对消息作任何处理,只是抛弃消息。
2.数据流向
in1从网络接收数据(通过头部默认的in),在的它的ChannelRead()方法中调用super.channelRead()方法把数据写入到pipeline中同一超类型的下一个in,就是in2。
在out2的write()方法中调用super.write(),会调用的是ctx.wriet(),所以会把数据写入out2的前一个out,就是out1,out1调用super.write(),把数据写入前一个out,就是默认的out。如果调用的是writeAndflush则会把数据写入网络。没有flush就不会写数据到网络.
In中调用write方法或writeAndFlush方法有两个方式
1. 调用channel的write或writeAndFlush方法,会执行尾端out的write方法。
源码如下,以调用writeAndFlush()来说明
channel的writeAndFlush会调用pipeline的writeAndFlush,
pipeline中会调用this.tail.writeAndFlush(),从尾端开始。
所以把in1中的channelRead()方法中的代码改成下图所示:
如上图所示,调用ctx.channel().writeAndFlush(),会导致从尾端out开始执行
此时执行顺序是in1,out2,out1,默认out,in2。
有两点要注意:
1.如果out1的write()方法中没有调用super.write(ctx,msg,promise);就不会执行默认的out,
此时执行顺序就是in1,out2,out1,in2。数据就不会写入到网络
2.如果out2的write()方法中没有调用super.write(ctx,msg,promise);就不会执行out1,也就不会执行默认out,此时执行顺序就是in1,in2,out2,数据就不会写入到网络。
因为代码super.channelRead()在ctx.channel().writeAndFlush()之后所在in2在out2之后执行,
如果super.channelRead()写在ctx.channel().writeAndFlush()前面,执行顺序就是in1,in2,out2,out1,默认out。
2. 调用ChannelHandlerContext的write或writeAndFlush方法,会执行当前的前一个out的writte()
源码如下,会找前一个out
把in1中的channelRead()方法中的代码改成下图所示:
如上图所示ctx.writeAndFlush(),会执行in1前一个out,就是默认的out,默认的out会直接把数据写回网络,注意如果调用的是ctx.write(),是不会写数据到网络的,只有flush才行。
此时执行顺序是in1,默认out,in2,
ctx.writeAndFlush()会写数据到网络。
如果把in2中的channelRead()方法中的代码改成下图所示:
如上图所示in2中ctx.writeAndFlush(),会执行in2之前的out的writeAndFlush(),就是out1,
此时执行顺序是in1,in2,out1,默认out。
总结下面两点,不管在in还是out中
1.要把数据传给下一个in,要调用ctx.fireChannelRead(msg)方法。
程序中可以这样写 super.channelRead(ctx, msg),本质上也是调用ctx.fireChannelRead(msg)方法。
2.要把数据传给out,调用channel或ctx的write方法。区别如下:
-
调用channel的write方法或writeAndFlush方法,会执行尾端的out的write方法。
调用示例:ctx.channel().writeAndFlush(msg) -
调用ChannelHandlerContext的write方法或writeAndFlush方法,会执行当前的前一个out的write方法。
调用示例:ctx.writeAndFlush(msg)
从中可以得出:
1.如果尾端的out没有调用ctx.write(),就不会执行尾端out之前的out,数据就不会写入到网络
out要把数据写入前一个out必须调用ctx.write(),程序中可以这样写super.write(ctx, msg, promise),调用super的write本质上也是调用ctx.write()
2.in中调用ctx.write()会把数据传给它的前一个out的write()方法。
in 中调用ctx.channel().write()会把数据传给尾端的out的write()方法.