Pipeline是基于责任链模式来设计的,其内部是一个双向链表结构,能够支持动态的添加和删除业务处理器
1、入站和出站处理流程
入站处理器流动次序:从前到后
出站处理器流动次序:从后到前
h1->h2->h3->h4->h5->h6
@Slf4j
public class TestPipeline {
public static void main(String[] args) {
new ServerBootstrap()
.group(new NioEventLoopGroup(),new NioEventLoopGroup(2))
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast("h1",new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("入站处理器h1");
super.channelRead(ctx, msg);
}
}).addLast("h2",new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("入站处理器h2");
super.channelRead(ctx, msg);
}
}).addLast("h3",new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("入站处理器h3");
super.channelRead(ctx, msg);
ch.writeAndFlush(ctx.alloc().buffer().writeBytes("server...".getBytes(StandardCharsets.UTF_8)));
//ChannelHandlerContext调用无法触发出站处理器
// ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("server...".getBytes(StandardCharsets.UTF_8)));
}
}).addLast("h4",new ChannelOutboundHandlerAdapter(){
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
log.info("出站处理器h4");
super.write(ctx, msg, promise);
}
}).addLast("h5",new ChannelOutboundHandlerAdapter(){
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
log.info("出站处理器h5");
super.write(ctx, msg, promise);
}
}).addLast("h6",new ChannelOutboundHandlerAdapter(){
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
log.info("出站处理器h6");
super.write(ctx, msg, promise);
}
});
}
})
.bind(8989);
}
}
输出
16:22:00.521 [nioEventLoopGroup-3-1] INFO com.jx.netty.netty.bootstrap.TestPipeline - 入站处理器h1
16:22:00.521 [nioEventLoopGroup-3-1] INFO com.jx.netty.netty.bootstrap.TestPipeline - 入站处理器h2
16:22:00.521 [nioEventLoopGroup-3-1] INFO com.jx.netty.netty.bootstrap.TestPipeline - 入站处理器h3
16:22:00.521 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message PooledUnsafeDirectByteBuf(ridx: 0, widx: 3, cap: 2048) that reached at the tail of the pipeline. Please check your pipeline configuration.
16:22:00.526 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [h1, h2, h3, h4, h5, h6, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0xd4642744, L:/127.0.0.1:8989 - R:/127.0.0.1:3432].
16:22:00.527 [nioEventLoopGroup-3-1] INFO com.jx.netty.netty.bootstrap.TestPipeline - 出站处理器h6
16:22:00.527 [nioEventLoopGroup-3-1] INFO com.jx.netty.netty.bootstrap.TestPipeline - 出站处理器h5
16:22:00.527 [nioEventLoopGroup-3-1] INFO com.jx.netty.netty.bootstrap.TestPipeline - 出站处理器h4
2、ChannelHandlerContext
ChannelHandlerContext
主要封装了ChannelHandler(通道处理器)和ChannelPipeline(通道流水线)之间的关联关系
Channel,Handler,ChannelHandlerContext三者关系:
Channel拥有一条ChannelPipeline
,流水线中每个流水线节点为一个ChannelHandlerContext
上下文对象,每个上下文对象包含了一个ChannelHandler
。
ChannelHandlerContext
包含很多方法,主要分为两类:
- 第一类是获取上下文所关联的Netty组件实例,如关联的通道,流水线,上下文内部Handler业务处理器实例等
- 第二类是入站和出站处理方法
Channel、ChannelPopeline、ChannelHandlerContext中都存在入站出站处理方法,功能有何不同? 如果通过Channel或者ChannelPopeline实例调用入站和出站处理方法,> 会在整条流水线传播> 。如果通过ChannelHandlerContext调用入站和出站方法,> 只会从当前节点开始往同一类型的下一个处理器传播。** 在上节示例入站处理器3中如果采用ChannelHandlerContext调用写消息方法,将不会经过出站处理器
3、HeadContext和TailContext
- 通道流水线在未加入任何业务处理器前装配了两个默认的处理器上下文:一个头部上下文
HeadContext
,一个尾部上下文TailContext
。 - 每个双向链表结构从一开始就存在了
HeadContext
和TailContext
两个节点,后面添加的处理器上下文节点都添加在HeadContext
和TailContext
实例之间。
protected DefaultChannelPipeline(Channel channel) {
this.channel = (Channel)ObjectUtil.checkNotNull(channel, "channel");
this.succeededFuture = new SucceededChannelFuture(channel, (EventExecutor)null);
this.voidPromise = new VoidChannelPromise(channel, true);
this.tail = new DefaultChannelPipeline.TailContext(this);
this.head = new DefaultChannelPipeline.HeadContext(this);
this.head.next = this.tail;
this.tail.prev = this.head;
}
3.1、TailContext
TailContext为一个入站处理器,实现了所有入站处理的回调方法,这些回调实现的主要工作基本上都是有关收尾处理的,如释放缓冲区对象、完成异常处理等。
final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
TailContext(DefaultChannelPipeline pipeline) {
super(pipeline, (EventExecutor)null, DefaultChannelPipeline.TAIL_NAME, DefaultChannelPipeline.TailContext.class);
this.setAddComplete();
}
public ChannelHandler handler() {
return this;
}
public void channelRegistered(ChannelHandlerContext ctx) {
}
public void channelUnregistered(ChannelHandlerContext ctx) {
}
public void channelActive(ChannelHandlerContext ctx) {
DefaultChannelPipeline.this.onUnhandledInboundChannelActive();
}
public void channelInactive(ChannelHandlerContext ctx) {
DefaultChannelPipeline.this.onUnhandledInboundChannelInactive();
}
public void channelWritabilityChanged(ChannelHandlerContext ctx) {
DefaultChannelPipeline.this.onUnhandledChannelWritabilityChanged();
}
public void handlerAdded(ChannelHandlerContext ctx) {
}
public void handlerRemoved(ChannelHandlerContext ctx) {
}
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
DefaultChannelPipeline.this.onUnhandledInboundUserEventTriggered(evt);
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
DefaultChannelPipeline.this.onUnhandledInboundException(cause);
}
public void channelRead(ChannelHandlerContext ctx, Object msg) {
DefaultChannelPipeline.this.onUnhandledInboundMessage(ctx, msg);
}
public void channelReadComplete(ChannelHandlerContext ctx) {
DefaultChannelPipeline.this.onUnhandledInboundChannelReadComplete();
}
}
3.2、HeadContext
既是一个出站处理器,也是一个入站处理器,还保存了一个unsafe实例(完成实际通道传输的类),也就是HeadContext还要负责最终的通道传输工作。
final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler {
//此类仅供Netty内部使用,应用程序不能用
private final Unsafe unsafe;
HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, (EventExecutor)null, DefaultChannelPipeline.HEAD_NAME, DefaultChannelPipeline.HeadContext.class);
this.unsafe = pipeline.channel().unsafe();
this.setAddComplete();
}
public ChannelHandler handler() {
return this;
}
public void handlerAdded(ChannelHandlerContext ctx) {
}
public void handlerRemoved(ChannelHandlerContext ctx) {
}
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
this.unsafe.bind(localAddress, promise);
}
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
this.unsafe.connect(remoteAddress, localAddress, promise);
}
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
this.unsafe.disconnect(promise);
}
public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
this.unsafe.close(promise);
}
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) {
this.unsafe.deregister(promise);
}
public void read(ChannelHandlerContext ctx) {
this.unsafe.beginRead();
}
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
this.unsafe.write(msg, promise);
}
public void flush(ChannelHandlerContext ctx) {
this.unsafe.flush();
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.fireExceptionCaught(cause);
}
public void channelRegistered(ChannelHandlerContext ctx) {
DefaultChannelPipeline.this.invokeHandlerAddedIfNeeded();
ctx.fireChannelRegistered();
}
public void channelUnregistered(ChannelHandlerContext ctx) {
ctx.fireChannelUnregistered();
if (!DefaultChannelPipeline.this.channel.isOpen()) {
DefaultChannelPipeline.this.destroy();
}
}
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
this.readIfIsAutoRead();
}
public void channelInactive(ChannelHandlerContext ctx) {
ctx.fireChannelInactive();
}
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.fireChannelRead(msg);
}
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.fireChannelReadComplete();
this.readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (DefaultChannelPipeline.this.channel.config().isAutoRead()) {
DefaultChannelPipeline.this.channel.read();
}
}
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
ctx.fireUserEventTriggered(evt);
}
public void channelWritabilityChanged(ChannelHandlerContext ctx) {
ctx.fireChannelWritabilityChanged();
}
}