备注:本文的分析基于netty 4.0.9final版本
1、ChannelPipeline结构图
2、关键类和接口分析
上一篇关于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();
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;
- @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;
- }
分析完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 { }
- }
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);
- }
- }
3、基于DefaultChannelPipeline的fireChannelActive函数分析handler的调用过程
1)fireChannelActive定义
- @Override
- public ChannelPipeline fireChannelActive() {
- head.fireChannelActive();
- if (channel.config().isAutoRead()) {
- channel.read();
- }
- return this;
- }
2) fireChannelActive实现分析
- @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函数的原因。
4、总结
本文分析了ChannelPipeline对Handler的管理过程,它的主要思路是利用ChannelHandlerContext上下文获取Handler,然后间接调用Handler的函数实现处理逻辑。