关于netty:netty系列之channelHandlerContext详解

38次阅读

共计 6508 个字符,预计需要花费 17 分钟才能阅读完成。

简介

咱们晓得 ChannelHandler 有两个十分重要的子接口,别离是 ChannelOutboundHandler 和 ChannelInboundHandler, 基本上这两个 handler 接口定义了所有 channel inbound 和 outbound 的解决逻辑。

不论是 ChannelHandler 还是 ChannelOutboundHandler 和 ChannelInboundHandler,简直他们中所有的办法都带有一个 ChannelHandlerContext 参数,那么这个 ChannelHandlerContext 到底是做什么用的呢?它和 handler、channel 有什么关系呢?

ChannelHandlerContext 和它的利用

相熟 netty 的敌人应该都接触过 ChannelHandlerContext,如果没有的话,这里有一个简略的 handler 的例子:

public class ChatServerHandler extends SimpleChannelInboundHandler<String> {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {log.info("accepted channel: {}", ctx.channel());
        log.info("accepted channel parent: {}", ctx.channel().parent());
        // channel 沉闷
        ctx.write("Channel Active 状态!\r\n");
        ctx.flush();}
}

这里的 handler 继承了 SimpleChannelInboundHandler,只须要实现对应的办法即可。这里实现的是 channelActive 办法,在 channelActive 办法中,传入了一个 ChannelHandlerContext 参数,咱们能够通过应用 ChannelHandlerContext 来调用它的一些办法。

先来看一下 ChannelHandlerContext 的定义:

public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {

首先 ChannelHandlerContext 是一个 AttributeMap,能够用来存储多个数据。

而后 ChannelHandlerContext 继承了 ChannelInboundInvoker 和 ChannelOutboundInvoker, 能够触发 inbound 和 outbound 的一些办法。

除了继承来的一些办法之外,ChannelHandlerContext 还能够作为 channel,handler 和 pipline 的沟通桥梁,因为能够从 ChannelHandlerContext 中获取到对应的 channel,handler 和 pipline:

Channel channel();
ChannelHandler handler();
ChannelPipeline pipeline();

还要留神的是 ChannelHandlerContext 还返回一个 EventExecutor,用来执行特定的工作:

EventExecutor executor();

接下来,咱们具体看一下 ChannelHandlerContext 的实现。

AbstractChannelHandlerContext

AbstractChannelHandlerContext 是 ChannelHandlerContext 的一个十分重要的实现,尽管 AbstractChannelHandlerContext 是一个抽象类,然而它基本上实现了 ChannelHandlerContext 的所有性能。

首先看一下 AbstractChannelHandlerContext 的定义:

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint

AbstractChannelHandlerContext 是 ChannelHandlerContext 的一个具体实现。

通常来说一个 handler 对应一个 ChannelHandlerContext,然而在一个程序中可能会有多于一个 handler,那么如何在一个 handler 中获取其余的 handler 呢?

在 AbstractChannelHandlerContext 中有两个同样是 AbstractChannelHandlerContext 类型的 next 和 prev, 从而使得多个 AbstractChannelHandlerContext 能够构建一个双向链表。从而能够在一个 ChannelHandlerContext 中,获取其余的 ChannelHandlerContext,从而取得 handler 解决链。

    volatile AbstractChannelHandlerContext next;
    volatile AbstractChannelHandlerContext prev;

AbstractChannelHandlerContext 中的 pipeline 和 executor 都是通过构造函数传入的:

    AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,
                                  String name, Class<? extends ChannelHandler> handlerClass) {this.name = ObjectUtil.checkNotNull(name, "name");
        this.pipeline = pipeline;
        this.executor = executor;
        this.executionMask = mask(handlerClass);
        // Its ordered if its driven by the EventLoop or the given Executor is an instanceof OrderedEventExecutor.
        ordered = executor == null || executor instanceof OrderedEventExecutor;
    }

可能有敌人会有疑难了,ChannelHandlerContext 中的 channel 和 handler 是如何失去的呢?

对于 channel 来说,是通过 pipeline 来获取的:

public Channel channel() {return pipeline.channel();
    }

对于 handler 来说,在 AbstractChannelHandlerContext 中并没有对其进行实现,须要在继承 AbstractChannelHandlerContext 的类中进行实现。

对于 EventExecutor 来说, 能够通过构造函数向 AbstractChannelHandlerContext 传入一个新的 EventExecutor,如果没有传入或者传入为空的话,则会应用 channel 中自带的 EventLoop:

    public EventExecutor executor() {if (executor == null) {return channel().eventLoop();} else {return executor;}
    }

因为 EventLoop 继承自 OrderedEventExecutor, 所以它也是一个 EventExecutor。

EventExecutor 次要用来异步提交工作来执行, 事实上 ChannelHandlerContext 中简直所有来自于 ChannelInboundInvoker 和 ChannelOutboundInvoker 的办法都是通过 EventExecutor 来执行的。

对于 ChannelInboundInvoker 来说,咱们以办法 fireChannelRegistered 为例:

    public ChannelHandlerContext fireChannelRegistered() {invokeChannelRegistered(findContextInbound(MASK_CHANNEL_REGISTERED));
        return this;
    }

    static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {next.invokeChannelRegistered();
        } else {executor.execute(new Runnable() {
                @Override
                public void run() {next.invokeChannelRegistered();
                }
            });
        }
    }

fireChannelRegistered 调用了 invokeChannelRegistered 办法,invokeChannelRegistered 则调用 EventExecutor 的 execute 办法,将实在的调用逻辑封装在一个 runnable 类中执行。

留神,在调用 executor.execute 办法之前有一个 executor 是否在 eventLoop 中的判断。如果 executor 曾经在 eventLoop 中了,那么间接执行工作即可,不须要启用新的线程。

对于 ChannelOutboundInvoker 来说,咱们以 bind 办法为例,看一下 EventExecutor 是怎么应用的:

    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {ObjectUtil.checkNotNull(localAddress, "localAddress");
        if (isNotValidPromise(promise, false)) {
            // cancelled
            return promise;
        }

        final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {next.invokeBind(localAddress, promise);
        } else {safeExecute(executor, new Runnable() {
                @Override
                public void run() {next.invokeBind(localAddress, promise);
                }
            }, promise, null, false);
        }
        return promise;
    }

能够看到执行的逻辑和 invokeChannelRegistered 办法很相似,也是先判断 executor 在不在 eventLoop 中,如果在的话间接执行,如果不在则放在 executor 中执行。

下面的两个例子中都调用了 next 的相应办法,别离是 next.invokeChannelRegistered 和 next.invokeBind。

咱们晓得 ChannelHandlerContext 只是一个封装,它自身并没有太多的业务逻辑,所以 next 调用的相应办法,实际上是 Context 中封装的 ChannelInboundHandler 和 ChannelOutboundHandler 中的业务逻辑,如下所示:

    private void invokeUserEventTriggered(Object event) {if (invokeHandler()) {
            try {((ChannelInboundHandler) handler()).userEventTriggered(this, event);
            } catch (Throwable t) {invokeExceptionCaught(t);
            }
        } else {fireUserEventTriggered(event);
        }
    }
    private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {if (invokeHandler()) {
            try {((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
            } catch (Throwable t) {notifyOutboundHandlerException(t, promise);
            }
        } else {bind(localAddress, promise);
        }
    }

所以,从 AbstractChannelHandlerContext 能够得悉,ChannelHandlerContext 接口中定义的办法都是调用的 handler 中具体的实现,Context 只是对 handler 的封装。

DefaultChannelHandlerContext

DefaultChannelHandlerContext 是 AbstractChannelHandlerContext 的一个具体实现。

咱们在解说 AbstractChannelHandlerContext 的时候提到过,AbstractChannelHandlerContext 中并没有定义具体的 handler 的实现,而这个实现是在 DefaultChannelHandlerContext 中进行的。

DefaultChannelHandlerContext 很简略,咱们看一下它的具体实现:

final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext {

    private final ChannelHandler handler;

    DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {super(pipeline, executor, name, handler.getClass());
        this.handler = handler;
    }

    @Override
    public ChannelHandler handler() {return handler;}
}

DefaultChannelHandlerContext 中额定提供了一个 ChannelHandler 属性,用来存储传入的 ChannelHandler。

到此 DefaultChannelHandlerContext 能够传入 ChannelHandlerContext 中所有必须的 handler,channel,pipeline 和 EventExecutor。

总结

本节咱们介绍了 ChannelHandlerContext 和它的几个根本实现,理解到了 ChannelHandlerContext 是对 handler,channel 和 pipline 的封装,ChannelHandlerContext 中的业务逻辑,实际上是调用的是底层的 handler 的对应办法。这也是咱们在自定义 handler 中须要实现的办法。

本文已收录于 http://www.flydean.com/04-4-netty-channelhandlercontext/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

正文完
 0