2. Netty 源码解析之ChannelPipeline

通过对 BootStrap 分析得到,在channel初始化时,会创造一个channelPipeline,代码如下:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    this.id = this.newId();//唯一标识
    this.unsafe = this.newUnsafe();
    this.pipeline = this.newChannelPipeline();
}

也知道了,每个channelPipeline实际是一个双重环形链表,代码如下:

protected DefaultChannelPipeline(Channel channel) {
    this.channel = (Channel)ObjectUtil.checkNotNull(channel, "channel");
    this.succeededFuture = new SucceededChannelFuture(channel, (EventExecutor)null);
    this.voidPromise = new VoidChannelPromise(channel, true);
    this.tail = new DefaultChannelPipeline.TailContext(this);
    this.head = new DefaultChannelPipeline.HeadContext(this);
    this.head.next = this.tail;
    this.tail.prev = this.head;
}

1. ChannelInitializer 作用

Bootstrap.init方法中会调用 p.addLast()方法,将 ChannelInitializer 插入到链表的末端,代码如下:

void init(Channel channel) throws Exception {
    ChannelPipeline p = channel.pipeline();
    p.addLast(new ChannelHandler[]{this.config.handler()});
    Map<ChannelOption<?>, Object> options = this.options0();
    synchronized(options) {
        setChannelOptions(channel, options, logger);
    }

    Map<AttributeKey<?>, Object> attrs = this.attrs0();
    synchronized(attrs) {
        Iterator var6 = attrs.entrySet().iterator();

        while(var6.hasNext()) {
            Entry<AttributeKey<?>, Object> e = (Entry)var6.next();
            channel.attr((AttributeKey)e.getKey()).set(e.getValue());
        }

    }
}

下面分析下 addLast方法,代码如下:

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    synchronized(this) {
        checkMultiplicity(handler);
        newCtx = this.newContext(group, this.filterName(name, handler), handler);
        this.addLast0(newCtx);
        if (!this.registered) {
            newCtx.setAddPending();
            this.callHandlerCallbackLater(newCtx, true);
            return this;
        }

        EventExecutor executor = newCtx.executor();
        if (!executor.inEventLoop()) {
            newCtx.setAddPending();
            executor.execute(new Runnable() {
                public void run() {
                    DefaultChannelPipeline.this.callHandlerAdded0(newCtx);
                }
            });
            return this;
        }
    }

    this.callHandlerAdded0(newCtx);
    return this;
}

代码中看到,newContext中创建了一个Context,创建的代码如下:

DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
    super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
    if (handler == null) {
        throw new NullPointerException("handler");
    } else {
        this.handler = handler;
    }
}
private static boolean isInbound(ChannelHandler handler) {
    return handler instanceof ChannelInboundHandler;
}

private static boolean isOutbound(ChannelHandler handler) {
    return handler instanceof ChannelOutboundHandler;
}

代码中,发现 ChannelInitializer 仅仅实现了 ChannelInboundHandler 接口,因此这里实例化的 DefaultChannelHandlerContext 的 inbound = true,outbound = false。

private void addLast0(AbstractChannelHandlerContext newCtx) {
    AbstractChannelHandlerContext prev = this.tail.prev;
    newCtx.prev = prev;
    newCtx.next = this.tail;
    prev.next = newCtx;
    this.tail.prev = newCtx;
}

2. 自定义 ChannelHandler 的添加过程

在channel注册调用链中,AbstractUnsafe.register0方法中调用了pipeline.fireChannelRegistered()方法进行添加自定义handler

private void register0(ChannelPromise promise) {
    try {
        if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
            return;
        }

        boolean firstRegistration = this.neverRegistered;
        AbstractChannel.this.doRegister();
        this.neverRegistered = false;
        AbstractChannel.this.registered = true;
        AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
        this.safeSetSuccess(promise);
        AbstractChannel.this.pipeline.fireChannelRegistered();
        if (AbstractChannel.this.isActive()) {
            if (firstRegistration) {
                AbstractChannel.this.pipeline.fireChannelActive();
            } else if (AbstractChannel.this.config().isAutoRead()) {
                this.beginRead();
            }
        }
    } catch (Throwable var3) {
        this.closeForcibly();
        AbstractChannel.this.closeFuture.setClosed();
        this.safeSetFailure(promise, var3);
    }

}
public final ChannelPipeline fireChannelRegistered() {
    AbstractChannelHandlerContext.invokeChannelRegistered(this.head);
    return this;
}
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeChannelRegistered();
    } else {
        executor.execute(new Runnable() {
            public void run() {
                next.invokeChannelRegistered();
            }
        });
    }

}
private void invokeChannelRegistered() {
    if (this.invokeHandler()) {
        try {
            ((ChannelInboundHandler)this.handler()).channelRegistered(this);
        } catch (Throwable var2) {
            this.notifyHandlerException(var2);
        }
    } else {
        this.fireChannelRegistered();
    }

}
private boolean invokeHandler() {
    int handlerState = this.handlerState;
    return handlerState == 2 || !this.ordered && handlerState == 1;
}
public ChannelHandlerContext fireChannelRegistered() {
    invokeChannelRegistered(this.findContextInbound());
    return this;
}
private AbstractChannelHandlerContext findContextInbound() {
    AbstractChannelHandlerContext ctx = this;

    do {
        ctx = ctx.next;
    } while(!ctx.inbound);

    return ctx;
}
private static final int ADD_PENDING = 1;
private static final int ADD_COMPLETE = 2;
private static final int REMOVE_COMPLETE = 3;

很显然,这个代码会从 head 开始遍历 Pipeline 的双向链表,然后找到第一个属性 inbound为 true 的 ChannelHandlerContext 实例。ChannelInitializer 实现了 ChannelInboudHandler,并接着调用了 ChannelInitializer.channelRegistered方法。
接下来看一下, ChannelInitializer 类内到底有什么玄机:

public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    if (this.initChannel(ctx)) {
        ctx.pipeline().fireChannelRegistered();
    } else {
        ctx.fireChannelRegistered();
    }
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    if (this.initMap.putIfAbsent(ctx, Boolean.TRUE) == null) {
        try {
            this.initChannel(ctx.channel());
        } catch (Throwable var6) {
            this.exceptionCaught(ctx, var6);
        } finally {
            this.remove(ctx);
        }

        return true;
    } else {
        return false;
    }
}
private void remove(ChannelHandlerContext ctx) {
        try {
            ChannelPipeline pipeline = ctx.pipeline();
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
        } finally {
            this.initMap.remove(ctx);
        }

    }

发现channelRegistered方法中调用了channel初始化的工作,即添加 ChannelHandler 到 ChannelPipeline,完成后会移除 ChannelInitializer。其中, ctx.pipeline().fireChannelRegistered()添加注册事件。

3. handler命名

关于handler的命名问题,通过跟踪代码发现,当没有自定义handler名称时,系统会自动创建一个命名,代码如下:

private String generateName(ChannelHandler handler) {
    Map<Class<?>, String> cache = (Map)nameCaches.get();
    Class<?> handlerType = handler.getClass();
    String name = (String)cache.get(handlerType);
    if (name == null) {
        name = generateName0(handlerType);
        cache.put(handlerType, name);
    }

    if (this.context0(name) != null) {
        String baseName = name.substring(0, name.length() - 1);
        int i = 1;

        while(true) {
            String newName = baseName + i;
            if (this.context0(newName) == null) {
                name = newName;
                break;
            }

            ++i;
        }
    }

    return name;
}
private static String generateName0(Class<?> handlerType) {
    return StringUtil.simpleClassName(handlerType) + "#0";
}

4. Pipeline 事件传输机制

官网中对于 channelpipeline 的介绍如下:

I/O Request
                                            via Channel or
                                        ChannelHandlerContext
                                                      |
  +---------------------------------------------------+---------------+
  |                           ChannelPipeline         |               |
  |                                                  \|/              |
  |    +---------------------+            +-----------+----------+    |
  |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  |               |                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  .               |
  |               .                                   .               |
  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
  |        [ method call]                       [method call]         |
  |               .                                   .               |
  |               .                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  |               |                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  +---------------+-----------------------------------+---------------+
                  |                                  \|/
  +---------------+-----------------------------------+---------------+
  |               |                                   |               |
  |       [ Socket.read() ]                    [ Socket.write() ]     |
  |                                                                   |
  |  Netty Internal I/O Threads (Transport Implementation)            |
  +-------------------------------------------------------------------+

描述了ChannelPipeline中ChannelHandler通常如何处理I/O事件。 I/O事件由ChannelInboundHandler或ChannelOutboundHandler处理,并通过调用ChannelHandlerContext中定义的事件传播方法。
从上图可以看出,inbound 事件和 outbound 事件的流向是不一样的,inbound 事件的流行是从下至上,而 outbound 刚好相反,是从上到下。并且 inbound 的传递方式是通过调用相应的 ChannelHandlerContext.fireIN_EVT()方法,而 outbound 方法的的传递方式是通过调用 ChannelHandlerContext.OUT_EVT()方法``。 例 如 ChannelHandlerContext.fireChannelRegistered()调用会发送一个 ChannelRegistered 的
inbound 给下一个 ChannelHandlerContext,而 ChannelHandlerContext.bind 调用会发送一个 bind 的 outbound 事件给下一个 ChannelHandlerContext。
Inbound 事件传播方法有:

ChannelHandlerContext.fireChannelRegistered()
ChannelHandlerContext.fireChannelActive()
ChannelHandlerContext.fireChannelRead(Object)
ChannelHandlerContext.fireChannelReadComplete()
ChannelHandlerContext.fireExceptionCaught(Throwable)
ChannelHandlerContext.fireUserEventTriggered(Object)
ChannelHandlerContext.fireChannelWritabilityChanged()
ChannelHandlerContext.fireChannelInactive()
ChannelHandlerContext.fireChannelUnregistered()

Outbound 事件传播方法有:

ChannelOutboundInvoker.bind(SocketAddress, ChannelPromise)
ChannelOutboundInvoker.connect(SocketAddress, SocketAddress, ChannelPromise)
ChannelOutboundInvoker.write(Object, ChannelPromise)
ChannelHandlerContext.flush()
ChannelHandlerContext.read()
ChannelOutboundInvoker.disconnect(ChannelPromise)
ChannelOutboundInvoker.close(ChannelPromise)
ChannelOutboundInvoker.deregister(ChannelPromise)

注意,如果我们捕获了一个事件,并且想让这个事件继续传递下去,那么需要调用 Context 相应的传播方法。

5. Outbound 的操作

Outbound 事件都是请求事件,传播方向是 tail -> 自定义context -> head。在 bootstrap 分析中,客户端段连接和服务器绑定socket都是outbound操作,具体相关代码如下:

public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return this.tail.bind(localAddress, promise);
}
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    } else if (this.isNotValidPromise(promise, false)) {
        return promise;
    } else {
        final AbstractChannelHandlerContext next = this.findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {// 判断执行线程(Thread.currentThread())是否是当前线程
            // 使用当前IO线程执行用户自定义处理
            next.invokeBind(localAddress, promise);
        } else {
            // 使用用户定义的线程执行处理过程
            safeExecute(executor, new Runnable() {
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, (Object)null);
        }

        return promise;
    }
}
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
    if (this.invokeHandler()) {//如果handler状态为可被调用
        try {
            ((ChannelOutboundHandler)this.handler()).bind(this, localAddress, promise);
        } catch (Throwable var4) {
            notifyOutboundHandlerException(var4, promise);
        }
    } else {
        this.bind(localAddress, promise);//等待,这里为啥设计成环形链表的原因,只要不是可调用状态,会一直循环这个环链表
    }

}

代码中,executor.inEventLoop()进行判断是否切换线程。Netty为用户提供了方便的线程切换处理,特别是在版本4.1后,可以直接使用JDK提供的线程池在一个处理器中如果有耗时较大的业务逻辑,可以指定该处理器的处理线程Executor,以避免占用IO线程造成性能损失。

private boolean invokeHandler() {
    // handlerState为volatile变量,存储为本地变量,以便减少volatile读
    int handlerState = this.handlerState;
    return handlerState == 2 || !this.ordered && handlerState == 1;
}

从代码中看出,当一个处理器还没有调用 HandlerAdded 方法时,或者处理器的执行线程是非顺序线程池的实例,才能执行业务处理逻辑;否则必须等待已调用 handlerAdded 方法,才能处理业务逻辑。这部分的处理,保证了ChannelPipeline的线程安全性,由此用户可以随意增加删除Handler。当可以处理业务逻辑时,调用 ChannelOutboundHandlerAdapter.bind,轮询到 HeadContext 时,调用它的bind方法做业务处理。

public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {
    ...
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        ctx.bind(localAddress, promise);
    }
    ...
}
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
    this.unsafe.bind(localAddress, promise);
}
protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        this.javaChannel().bind(localAddress, this.config.getBacklog());
    } else {
        this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
    }

}

6. Inbound 事件

Inbound 事件是一个通知事件,传播方向是 head -> 自定义Context -> tail。怎么找到 Outbound 和 Inbound 事件之间的联系呢?
还以bind为例,具体业务逻辑处理过程,代码如下:

public final void bind(SocketAddress localAddress, ChannelPromise promise) {
    this.assertEventLoop();
    if (promise.setUncancellable() && this.ensureOpen(promise)) {
        ...
        boolean wasActive = AbstractChannel.this.isActive();

        try {
            AbstractChannel.this.doBind(localAddress);
        } catch (Throwable var5) {
            this.safeSetFailure(promise, var5);
            this.closeIfClosed();
            return;
        }

        if (!wasActive && AbstractChannel.this.isActive()) {
            this.invokeLater(new Runnable() {
                public void run() {
                    AbstractChannel.this.pipeline.fireChannelActive();
                }
            });
        }

        this.safeSetSuccess(promise);
    }
}

doBind绑定完socket之后,会调用 AbstractChannel.this.pipeline.fireChannelActive()进行inbound事件,将socket绑定成功的消息传递出去,消息是从head开始的。接着会调用 ChannelInboundHandlerAdapter.fireChannelActive,轮询handler,直到 TailContext 将消息传递出去。

public final ChannelPipeline fireChannelActive() {
    AbstractChannelHandlerContext.invokeChannelActive(this.head);
    return this;
}
public ChannelHandlerContext fireChannelActive() {
    invokeChannelActive(this.findContextInbound());
    return this;
}
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    DefaultChannelPipeline.this.onUnhandledInboundChannelActive();
}

TailContext 的 Inbound 处理方法时,它们的实现都是空的,也就是说如果是 Inbound,当用户没有实现自定义的处理器时,那么默认是不处理的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值