备注:本文的分析基于netty 4.0.9final版本
上一篇关于Channel的文章,在AbstractChannel的介绍中,以及提到了pipeline,这是操作处理的入口,是一个比较重要的概念,这里有必要对pipeline分析一下。
1)ChannelInboundInvoker
/** * Interface which is shared by others which need to fire inbound events */ interface ChannelInboundInvoker { /** * A {@link Channel} was registered to its {@link EventLoop}. * * This will result in having the {@link ChannelInboundHandler#channelRegistered(ChannelHandlerContext)} method * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelRegistered(); /** * A {@link Channel} was unregistered from its {@link EventLoop}. * * This will result in having the {@link ChannelInboundHandler#channelUnregistered(ChannelHandlerContext)} method * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ @Deprecated ChannelInboundInvoker fireChannelUnregistered(); /** * A {@link Channel} is active now, which means it is connected. * * This will result in having the {@link ChannelInboundHandler#channelActive(ChannelHandlerContext)} method * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelActive(); /** * A {@link Channel} is inactive now, which means it is closed. * * This will result in having the {@link ChannelInboundHandler#channelInactive(ChannelHandlerContext)} method * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelInactive(); /** * A {@link Channel} received an {@link Throwable} in one of its inbound operations. * * This will result in having the {@link ChannelInboundHandler#exceptionCaught(ChannelHandlerContext, Throwable)} * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireExceptionCaught(Throwable cause); /** * A {@link Channel} received an user defined event. * * This will result in having the {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireUserEventTriggered(Object event); /** * A {@link Channel} received a message. * * This will result in having the {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)} * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelRead(Object msg); ChannelInboundInvoker fireChannelReadComplete(); /** * Triggers an {@link ChannelInboundHandler#channelWritabilityChanged(ChannelHandlerContext)} * event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. */ ChannelInboundInvoker fireChannelWritabilityChanged(); }这接口定义比较简单,即有事件数据从底层到应用时,用它来做相应的处理。
2)ChannelOutboundInvoker
/** * Interface which is shared by others which need to execute outbound logic. */ interface ChannelOutboundInvoker { /** * Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation * completes, either because the operation was successful or because of an error. * <p> * This will result in having the * {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method * called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture bind(SocketAddress localAddress); /** * Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation * completes, either because the operation was successful or because of an error. * <p> * If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with * a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException} * will be used. * <p> * This will result in having the * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress); /** * Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of * an error. * <p> * This will result in having the * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress); /** * Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes, * either because the operation was successful or because of an error. * <p> * This will result in having the * {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture disconnect(); /** * Request to close this ChannelOutboundInvoker and notify the {@link ChannelFuture} once the operation completes, * either because the operation was successful or because of * an error. * * After it is closed it is not possible to reuse it again. * <p> * This will result in having the * {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture close(); /** * Request to deregister this ChannelOutboundInvoker from the previous assigned {@link EventExecutor} and notify the * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of * an error. * <p> * This will result in having the * {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. * */ @Deprecated ChannelFuture deregister(); /** * Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation * completes, either because the operation was successful or because of an error. * * The given {@link ChannelPromise} will be notified. * <p> * This will result in having the * {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method * called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise); /** * Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation * completes, either because the operation was successful or because of an error. * * The given {@link ChannelFuture} will be notified. * * <p> * If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with * a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException} * will be used. * <p> * This will result in having the * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise); /** * Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of * an error. * * The given {@link ChannelPromise} will be notified and also returned. * <p> * This will result in having the * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise); /** * Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes, * either because the operation was successful or because of an error. * * The given {@link ChannelPromise} will be notified. * <p> * This will result in having the * {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture disconnect(ChannelPromise promise); /** * Request to close this ChannelOutboundInvoker and notify the {@link ChannelFuture} once the operation completes, * either because the operation was successful or because of * an error. * * After it is closed it is not possible to reuse it again. * The given {@link ChannelPromise} will be notified. * <p> * This will result in having the * {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture close(ChannelPromise promise); /** * Request to deregister this ChannelOutboundInvoker from the previous assigned {@link EventExecutor} and notify the * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of * an error. * * The given {@link ChannelPromise} will be notified. * <p> * This will result in having the * {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ @Deprecated ChannelFuture deregister(ChannelPromise promise); /** * Request to Read data from the {@link Channel} into the first inbound buffer, triggers an * {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)} event if data was * read, and triggers a * {@link ChannelInboundHandler#channelReadComplete(ChannelHandlerContext) channelReadComplete} event so the * handler can decide to continue reading. If there's a pending read operation already, this method does nothing. * <p> * This will result in having the * {@link ChannelOutboundHandler#read(ChannelHandlerContext)} * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelOutboundInvoker read(); /** * Request to write a message via this ChannelOutboundInvoker through the {@link ChannelPipeline}. * This method will not request to actual flush, so be sure to call {@link #flush()} * once you want to request to flush all pending data to the actual transport. */ ChannelFuture write(Object msg); /** * Request to write a message via this ChannelOutboundInvoker through the {@link ChannelPipeline}. * This method will not request to actual flush, so be sure to call {@link #flush()} * once you want to request to flush all pending data to the actual transport. */ ChannelFuture write(Object msg, ChannelPromise promise); /** * Request to flush all pending messages via this ChannelOutboundInvoker. */ ChannelOutboundInvoker flush(); /** * Shortcut for call {@link #write(Object, ChannelPromise)} and {@link #flush()}. */ ChannelFuture writeAndFlush(Object msg, ChannelPromise promise); /** * Shortcut for call {@link #write(Object)} and {@link #flush()}. */ ChannelFuture writeAndFlush(Object msg); }这个接口定义用于处理从应用到底层的事件数据。
3)ChannelPipeline
ChannelPipeline继承ChannelInboundInvoker和ChannelOutboundInvoker,它既是一个inboundinvoke,又是一个outboundinvoke,同时它也是ChannelHandler的管理者,提供了很多方法对handler进行操作。
4)DefaultChannelPipeline
在分析DefaultChannelPipeline之前,不得不先分析DefaultChannelHandlerContext(实现ChannelHandlerContext),因为ChannelPipeline的所有handler执行都是间接由ChannelhandlerContext执行的,在后面DefaultChannelPipeline的定义中,我们可以看到这点。
ChannelHandlerContext接口定义了很多方法,关键代码如下:
/** * Return the {@link Channel} which is bound to the {@link ChannelHandlerContext}. */ Channel channel(); /** * The {@link EventExecutor} that is used to dispatch the events. This can also be used to directly * submit tasks that get executed in the event loop. For more information please refer to the * {@link EventExecutor} javadoc. */ EventExecutor executor();返回当前HandlerContext的Channel,以及返回当前EventExcutor。
DefaultChannelHandlerContext具体的final类,关键代码如下:
volatile DefaultChannelHandlerContext next; volatile DefaultChannelHandlerContext prev; private final boolean inbound; private final boolean outbound; private final AbstractChannel channel; private final DefaultChannelPipeline pipeline; private final String name; private final ChannelHandler handler; private boolean removed; // Will be set to null if no child executor should be used, otherwise it will be set to the // child executor. final EventExecutor executor; private ChannelFuture succeededFuture;从上述代码中可以看出,DefaultChannelHandlerContext其实是一个上下文环境,它里面包括了当前Channel,当前的pipeline,以及当前的handler。另外它还提供了很多方法的实现,列如下面的函数。
@Override public ChannelHandlerContext fireChannelActive() { final DefaultChannelHandlerContext next = findContextInbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeChannelActive(); } else { executor.execute(new Runnable() { @Override public void run() { next.invokeChannelActive(); } }); } return this; }从这个函数中,可以看出Handler的执行是由executor以线程的方式执行的。
分析完DefaultChannelHandlerContext,下面再来看DefaultChannelPipeline(实现Channelpipeline)。它是一个具体的handler管理者,handler的类型可以是inboundhandler,也可以是outboundhandler。关键代码如下:
final AbstractChannel channel; final DefaultChannelHandlerContext head; final DefaultChannelHandlerContext tail; private final Map<String, DefaultChannelHandlerContext> name2ctx = new HashMap<String, DefaultChannelHandlerContext>(4); final Map<EventExecutorGroup, EventExecutor> childExecutors = new IdentityHashMap<EventExecutorGroup, EventExecutor>(); public DefaultChannelPipeline(AbstractChannel channel) { if (channel == null) { throw new NullPointerException("channel"); } this.channel = channel; TailHandler tailHandler = new TailHandler(); tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler); HeadHandler headHandler = new HeadHandler(channel.unsafe()); head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler); head.next = tail; tail.prev = head; }
从上述代码中可以看出,每个Channel都有一个对应的pipeline,每个pipeline有两个ChannelHandlerContext,分别包含默认的TailHandler和HeadHandler,并且它们构成一个双向链表结构。TailHandler处理inbound类型的数据;HeadHandler处理outbound类型的数据。
TailHandler是一个内部类,它实现ChannelInboundHandler接口,定义如下:
// A special catch-all handler that handles both bytes and messages. static final class TailHandler implements ChannelInboundHandler { @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.warn( "An exceptionCaught() event was fired, and it reached at the tail of the pipeline. " + "It usually means the last handler in the pipeline did not handle the exception.", cause); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { logger.debug( "Discarded inbound message {} that reached at the tail of the pipeline. " + "Please check your pipeline configuration.", msg); } finally { ReferenceCountUtil.release(msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } }TailHandler的实现函数都是空的,这说明对于底层上来应用的数据,用户必须定义Handler来处理,不能使用默认的Handler进行处理。
HeadHandler也是一个内部类,它实现ChannelOutboundHandler接口,定义如下:
static final class HeadHandler implements ChannelOutboundHandler { protected final Unsafe unsafe; protected HeadHandler(Unsafe unsafe) { this.unsafe = unsafe; } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // NOOP } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // NOOP } @Override public void bind( ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.bind(localAddress, promise); } @Override public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.connect(remoteAddress, localAddress, promise); } @Override public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.disconnect(promise); } @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.close(promise); } @Override public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.deregister(promise); } @Override public void read(ChannelHandlerContext ctx) { unsafe.beginRead(); } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { unsafe.write(msg, promise); } @Override public void flush(ChannelHandlerContext ctx) throws Exception { unsafe.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.fireExceptionCaught(cause); } }HeaderHandler的实现函数都是基于unsafe对象的函数实现的,所以对于OutBound类型的数据,即应用往底层的数据,可以使用默认的Handler进行处理。
1)fireChannelActive定义
@Override public ChannelPipeline fireChannelActive() { head.fireChannelActive(); if (channel.config().isAutoRead()) { channel.read(); } return this; }
@Override public ChannelHandlerContext fireChannelActive() { final DefaultChannelHandlerContext next = findContextInbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeChannelActive(); } else { executor.execute(new Runnable() { @Override public void run() { next.invokeChannelActive(); } }); } return this; }<pre name="code" class="java"> private DefaultChannelHandlerContext findContextInbound() { DefaultChannelHandlerContext ctx = this; do { ctx = ctx.next; } while (!ctx.inbound); return ctx; }
private void invokeChannelActive() { try { ((ChannelInboundHandler) handler).channelActive(this); } catch (Throwable t) { notifyHandlerException(t); } }
上面的代码逻辑还是比较简单的,就是从Channelhandlercontext的Head链表中获取上下文,然后调用相应的Handler执行函数。作为开发者,这时必须定义自己的Handler,并且实现ChannelActive函数,因为这是从底层到应用的数据,这就是平时我们在编写服务器端的代码时,都会定义一个继承InboundHandler的handler,并且覆盖channelActive函数的原因。
本文分析了ChannelPipeline对Handler的管理过程,它的主要思路是利用ChannelHandlerContext上下文获取Handler,然后间接调用Handler的函数实现处理逻辑。