Netty 源码(ChannelHandler 死磕)

精进篇:netty源码死磕5  - 揭开 ChannelHandler 的神秘面纱html

目录

1. 前言
2. Handler在经典Reactor中的角色
3. Handler在Netty中的坐标位置
4. Netty中Handler的类型

1.1. ChannelInboundHandler入站处理器
1.2. ChannelOutboundHandler出站处理器
5. 揭开Pipeline的神秘面纱
6. Handler的上下文环境

7. Handler的注册
7.1. 第一步:包裹
7.2. 加入链表并注册完成回调事件
7.3. 回调添加完成事件
8. 小结
编程


1. 前言

Reactor模式是Netty的基础和灵魂,掌握了经典的Reactor模式实现,完全掌握Netty就事半功倍了。《Reactor模式(netty源码死磕3)》对Reactor模式的经典实现,进行了详细介绍。做为本文的阅读准备,能够去温习一下。设计模式

Reactor模式的两个重要的组件,一个是Reactor反应器,在Netty中的对应的实现是EventLoop,在文章《EventLoop(netty源码死磕4)》中,已经有了很是详细的介绍。缓存

此文聚焦于Reactor模式的另外一个重要的组成部分Handler。网络

2. Handler在经典Reactor中的角色

在Reactor经典模型中,Reactor查询到NIO就绪的事件后,分发到Handler,由Handler完成NIO操做和计算的操做。数据结构

wps131D.tmp

Handler主要的操做为Channel缓存读、数据解码、业务处理、写Channel缓存,而后由Channel(表明client)发送到最终的链接终端。ide

3. Handler在Netty中的坐标

经典的Reactor模式,更多在于演示和说明,仅仅是有一种浓缩和抽象。函数

因为Netty更多用于生产,在实际开发中的业务处理这块,主要经过Handler来实现,因此Netty中在Handler的组织设计这块,远远比经典的Reactor模式实现,要纷繁复杂得多。oop

wps133D.tmp


在分析Handler以前,首先回顾一下Netty中的Channel。在《EventLoop(netty源码死磕4)》中,已经有详细的说明。一个Netty Channel对应于一个Client链接,内部封装了一个Java NIO SelectableChannel 可查询通道。学习

再回到Handler。

Hander的根本使命,就是处理Channel的就绪事件,根据就绪事件,完成NIO处理和业务操做。比方Channel读就绪时,Hander就开始读;Channel写就绪时,Hander就开始写。

4. Netty中Handler的类型

从应用程序开发人员的角度来看,Netty的主要组件是ChannelHandler,因此,对ChannelHandler的分类,也是从应用开发的角度来的。

wps134E.tmp

从应用程序开发人员的角度来看,数据有入站和出站两种类型。

这里的出站和入站,不是网络通讯方面的入站和出站。而是相对于Netty Channel与Java NIO Channel而言的。

数据入站,指的是数据从底层的Java NIO channel到Netty的Channel。数据出站,指的是经过Netty的Channel来操做底层的 Java NIO chanel。

从入站和出战的角度出发,Netty中的ChannelHandler主要由两种类型,ChannelInboundHandler和ChannelOutboundHandler。

1.1. ChannelInboundHandler入站处理器

当Java NIO事件进站到Channel时,产生一的一系列事件将由ChannelHandler所对应的API处理。

当查询到Java NIO底层Channel的就绪事件时,经过一系列的ChannelInboundHandler处理器,完成底层就绪事件的处理。比方说底层链接创建事件、底层链接断开事件、从底层读写就绪事件等等。

wps136E.tmp


啰嗦一下,入站(inbound)处理一般由底层Java NIO channel触发,主要事件以下:

1. 注册事件 fireChannelRegistered。

2. 链接创建事件 fireChannelActive。

3. 读事件和读完成事件 fireChannelRead、fireChannelReadComplete。

4. 异常通知事件 fireExceptionCaught。

5. 用户自定义事件 fireUserEventTriggered。

6. Channel 可写状态变化事件 fireChannelWritabilityChanged。

7. 链接关闭事件 fireChannelInactive。


1.2. ChannelOutboundHandler出站处理器


当须要Netty Channel须要操做Java NIO底层Channel时,经过一系列的ChannelOutboundHandler处理器,完成底层操做。比方说创建底层链接、断开底层链接、从底层Java NIO通道读入、写入底层Java NIO通道等。ChannelOutboundHandler是一个接口,主要操做以下图所示:

wps137F.tmp



啰嗦一下,出站(inbound) Handler一般是Netty channel操做底层Java NIO channel,主要操做以下:

1. 端口绑定 bind。

2. 链接服务端 connect。

3. 写事件 write。

4. 刷新时间 flush。

5. 读事件 read。

6. 主动断开链接 disconnect。

7. 关闭 channel 事件 close。

至此,Netty中的两大处理器的类型,就已经说得很清楚了。

再说说Handler和Channel的关系。

打个比方,若是Hander是太阳系的行星,那么Channel就是太阳系的恒星。Hander的服务对象和公转的轴心,就是Channel。

这多是最为不恰当的一个比方,可是说的是事实。


5. 揭开Pipeline的神秘面纱

一个Channel在数量上,确定不止拥有一个Handler。 如何将杂乱无章的Handler,有序的组织起来呢?

来了一个Handler的装配器——Pipeline。

Pipeline是何方神圣呢?

先揭一下神秘面纱:

Netty中, 使用一个双向链表,将属于一个Channel的全部Handler组织起来,而且给这个双向链表封装在一个类中,再给这个类取了一个很是牛逼的名字,叫作ChannelPipeline。

为何这个名字很牛逼呢?

实际上这里用了Java中一种很是重要的设计模式,Pipeline设计模式。后面将用专门的文章,来介绍这种牛逼模式。

回到主题:

一个Channel,仅仅一个ChannelPipeline。该pipeline在Channel被建立的时候建立。ChannelPipeline至关因而ChannelHandler的容器,它包含了一个ChannelHander造成的列表,且全部ChannelHandler都会注册到ChannelPipeline中。


6. Handler的上下文环境

在Netty的设计中,Handler是无状态的,不保存和Channel有关的信息。打个不恰当的比方,Handler就像国际雇佣军同样,谁给钱,给谁打仗。Handler的目标,是将本身的处理逻辑作得很完成,能够给不一样的Channel使用。

与之不一样的是,Pipeline是有状态的,保存了Channel的关系。

因而乎,Handler和Pipeline之间,须要一个中间角色,把他们联系起来。这个中间角色是谁呢?

它就是——ChannelHandlerContext 。

因此,ChannelPipeline 中维护的,是一个由 ChannelHandlerContext 组成的双向链表。这个链表的头是 HeadContext, 链表的尾是 TailContext。而无状态的Handler,做为Context的成员,关联在ChannelHandlerContext 中。在对应关系上,每一个 ChannelHandlerContext 中仅仅关联着一个 ChannelHandler。

wps138F.tmpwps13B0.tmp



咱们继续用源码说话。

Context的双向链表的主要代码,在 AbstractChannelHandlerContext类中。该类主要包含一个双向链表节点的前置和后置节点引用 prev、next,以及数据引用 handler,至关于链表数据结构中的 Node 节点。

部分关键源码节选以下:

// ChannelHandler 首位指针
    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;
    // pipeline 所属 channel
    private final Channel channel;
    private final ChannelFuture succeededFuture;
    private final VoidChannelPromise voidPromise;
    private final boolean touch = ResourceLeakDetector.isEnabled();

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;
    }

7. Handler的注册

Handler是如何注册到Pipeline中的呢?

1.3. 第一步:包裹

加入到Pipeline以前,在Pipeline的基类DefaultChannelPipeline中,首先对Handler进行包裹。

代码以下:

// 使用 AbstractChannelHandlerContext 包裹 ChannelHandler

private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
    return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}

1.4. 加入链表并注册完成回调事件

1. 构建了 AbstractChannelHandlerContext 节点,并加入到了链表尾部。

2. 若是 channel 还没有注册到 EventLoop,就添加一个任务到 PendingHandlerCallback 上,后续channel 注册完毕,再调用 ChannelHandler.handlerAdded。

3. 若是已经注册,立刻调用 callHandlerAdded0 方法来执行 ChannelHandler.handlerAdded 注册完成的回调函数。

代码以下:

@Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            // 检查,若不是 Sharable,并且已经被添加到其余 pipeline,则抛出异常
            checkMultiplicity(handler);
            // 构建 AbstractChannelHandlerContext 节点
            newCtx = newContext(group, filterName(name, handler), handler);
            // 添加到链表尾部
            addLast0(newCtx);

            // registered 为 false 表示 channel 还没有注册到 EventLoop 上。
            // 添加一个任务到 PendingHandlerCallback 上,后续注册完毕,再调用 ChannelHandler.handlerAdded
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            // registered 为 true,则当即调用 ChannelHandler.handlerAdded
            EventExecutor executor = newCtx.executor();
            // inEvent 用于判断当前线程是不是 EventLoop 线程。执行 ChannelHandler 时,必须在对应的 EventLoop 线程池中执行。
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

 @Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            // 检查,若不是 Sharable,并且已经被添加到其余 pipeline,则抛出异常
            checkMultiplicity(handler);
            // 构建 AbstractChannelHandlerContext 节点
            newCtx = newContext(group, filterName(name, handler), handler);
            // 添加到链表尾部
            addLast0(newCtx);

            // registered 为 false 表示 channel 还没有注册到 EventLoop 上。
            // 添加一个任务到 PendingHandlerCallback 上,后续注册完毕,再调用 ChannelHandler.handlerAdded
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

     // registered 为 true,则当即调用 ChannelHandler.handlerAdded
            EventExecutor executor = newCtx.executor();
            // inEvent 用于判断当前线程是不是 EventLoop 线程。执行 ChannelHandler 时,必须在对应的 EventLoop 线程池中执行。
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

1.5. 回调添加完成事件

添加完成后,执行回调方法以下:

private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
        try {
ctx.handler().handlerAdded(ctx);
            ctx.setAddComplete();
        } catch (Throwable t) {
           …….
    }

会执行handler的handlerAdded 方法,这是一个回调方法。添加完成后的回调代码,基本上写在这里。



8. 小结


至此,牛逼的Netty Handler和Netty Reactor 介绍完了。

对于Pipeline模式和基于Pipeline的Netty 入站和出站的事件传输机制,【疯狂创客圈】在后面的系列死磕文章,会作一个很是精彩的介绍。




无编程不创客,无案例不学习。疯狂创客圈,一大波高手正在交流、学习中!

疯狂创客圈 Netty 死磕系列 10多篇深度文章博客园 总入口】  QQ群:104131248