Java教程:Netty知识点详解(十八)

开课吧开课吧锤锤2021-02-24 15:06

近年来,随着云计算、大数据等热门技术的发展和普及,Java在企业中越来越受到重视,公司对于Java人才需求也是每年都在增长,对于这样热门的行业,很多人都是心动的,那么对于如何学习,怎么提高Java的技术,那就来看看本篇文章吧。

Pipeline

前面说了那么学完,大家对于 Netty 执行流程,Server 端和 Client 端的启动,大家都有了很深的认识,前面也留了很大一块关于 Netty 服务启动之后的处理过程,这部分也就是前面没有说的 Pipeline 的部分知识点。下面我们就重点说说 Pipeline 。

Pipeline 的创建

先找到代码,Pipeline 的创建其实在前面也有看到,这个入口就在Server 端和 Client 端启动的时创建 Channel 的时候。找到代码:io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)

protected AbstractChannel(Channel parent) {

    this.parent = parent;

    // 为channel生成id,由五部分构成

    id = newId();

    // 生成一个底层操作对象unsafe

    unsafe = newUnsafe();

    // 创建与这个channel相绑定的channelPipeline

    pipeline = newChannelPipeline();

}

newChannelPipeline() 这个就是 Pipeline 的创建入口,跟进去:

protected DefaultChannelPipeline newChannelPipeline() {

    return new DefaultChannelPipeline(this);

}

可以看到这里是直接创建了 DefaultChannelPipeline。直接找到构造方法:

protected DefaultChannelPipeline(Channel channel) {

    this.channel = ObjectUtil.checkNotNull(channel, "channel");

    succeededFuture = new SucceededChannelFuture(channel, null);

    voidPromise =  new VoidChannelPromise(channel, true);

    // 创建尾节点

    tail = new TailContext(this);

    // 创建头节点

    head = new HeadContext(this);

    // 将头尾节点连接

    head.next = tail;

    tail.prev = head;

}

1、创建一个记录成功的 succeededFuture 。

2、创建一个记录异常的 voidPromise,在 VoidChannelPromise 方法中创建了一个异常监听器,触发重写的 fireExceptionCaught(cause) 的方法执行。

3、创建一个尾节点。

4、创建一个头节点。

5、将收尾节点关联起来。

这里我们去看看两个 TailContext 和 HeadContext。两个差不多,先看 new TailContext(this);.这里 TailContext 是一个内部类:

Java

实现了 ChannelInboundHandler 处理器,是一个 InboundHandler。关于 InboundHandler 和 OutboundHandler 处理器下面单独说,这里不展开。只要知道 InboundHandler 的方法都是处理回调的方法。

这里还是看 TailContext 的构造方法。第一步调用了父类构造,然后修改节点的处理器状态,先进去看看修改节点处理器状态的方法:

final boolean setAddComplete() {

    for (;;) {

        int oldState = handlerState; // 获取处理器状态

        if (oldState == REMOVE_COMPLETE) { // 处理器状态为移除状态

            return false;

        }

        // 通过CAS方式将处理器状态修改为 添加完毕

        if (HANDLER_STATE_UPDATER.compareAndSet(this, oldState, ADD_COMPLETE)) {

            return true;

        }

    }

}

就是通过 CAS 的方式将当前节点的处理器状态修改为添加完毕。然后我们再回去跟父类构造:

AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,

                              String name, Class<? extends ChannelHandler> handlerClass) {

    this.name = ObjectUtil.checkNotNull(name, "name");

    this.pipeline = pipeline;

    // 每个处理器节点都会绑定一个executor

    this.executor = executor;

    // 执行标记

    this.executionMask = mask(handlerClass);

    ordered = executor == null || executor instanceof OrderedEventExecutor;

}

这里先是绑定 pipeline 和 executor,不过这里的 executor 传入是 null,每个处理器节点都会绑定一个 Ececutor ,如果当前处理器的 executor 为空则直接使用 channel 的 Executor 来执行当前处理器节点里的处理器方法。

这里我们跟进去看下 mask(handlerClass) 方法:

static int mask(Class<? extends ChannelHandler> clazz) {

    // 从缓存中尝试着获取标记

    Map<Class<? extends ChannelHandler>, Integer> cache = MASKS.get();

    Integer mask = cache.get(clazz);

    if (mask == null) {

        // 创建一个标记

        mask = mask0(clazz);

        cache.put(clazz, mask);

    }

    return mask;

}

可以看到这个方法是一个静态的,首先从缓存中获取标记数据,获取不到为当前处理器类创建缓存标记:mask0(clazz):

Java

这是充分使用了二进制的开关的性质,这里方法的作用就是将所有的 InboundHandler 处理器和 OutboundHandler 处理器中定义的方法进行标记,如果其中的方法被实现了,并且方法中没有 @Skip 注解,则当前方法对应的二进制位的值是 1,当当前标记位等于 1 时,则标记当前方法时需要执行的。

Java

其实这里在 TailContext 和 HeadContext 中所有的标记位都是 1,因为 TailContext 和 HeadContext 分别都实现了 InboundHandler 和 OutboundHandler 接口中的接口。这里说这个主要因为这里在我们自定义的处理器时就会使用到。扎到一个自定义的处理器:

Java

我们没有直接实现自 InboundHandler,而是直接继承了 ChannelInboundHandlerAdapter ,大家可以进去看看,在 ChannelInboundHandlerAdapter 中每一个实现方法上都有一个 @Skip 注解,而且它默认实现了所有的 InboundHandler 接口的方法,就是为了我们在定义自定义处理器减少一些默认实现的处理,而且为了性能在初始化时将所有的方法打上标记,保证只执行我们自己实现的方法,这就是这个标记的用处。这里 mask 处理的都是 InboundHandler 和 OutboundHandler 处理器中的接口方法。

好了到这里 TailContext 节点创建完成,我们接着看 HeadContext 节点:

// 头节点既是inbound处理器,也是outbound处理器

final class HeadContext extends AbstractChannelHandlerContext

            implements ChannelOutboundHandler, ChannelInboundHandler {

        private final Unsafe unsafe;

        HeadContext(DefaultChannelPipeline pipeline) {

            super(pipeline, null, HEAD_NAME, HeadContext.class);

            unsafe = pipeline.channel().unsafe();

            setAddComplete();

        }

        // 。。。。

HeadContext 也是内部类,这里与 TailContext 不同的是,HeadContext 同时实现了 InboundHandler 和 OutboundHandler。并且创建了一个用于底层 IO 处理的 unsafe 对象。到这里 Pipeline 的初始化创建看完了,可以看到 Pipeline 在 Channel 的创建的时初始化创建的。

下面说一下一个特殊的处理器,前面也是一直看到:ChannelInitializer处理器。

以上内容由开课吧老师敖丙提供,更多Java教程尽在开课吧广场Java教程频道。更多免费课程可以关注公众号“码农集散地”

有用
分享