Netty's ChannelPipeline (3) Propagation of Outbound and Inbound Events

  • Outbound events are request events.
  • The initiator of the outbound event is Channel, and the handler is Unsafe.
  • The outbound event transmission direction in Pipeline is tail-> head.

One of the Outbount events bind, take bind as an example:
AbstractChannel
bind (SocketAddress localAddress, ChannelPromise promise):

@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return pipeline.bind(localAddress, promise);
}
  1. This method is implemented from the ChannelOutboundInvoker interface.
  2. The method internal call is called by the bind (SocketAddress localAddress, ChannelPromise promise) method.

Look at the specific implementation of bind (SocketAddress localAddress, ChannelPromise promise) method:

@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return tail.bind(localAddress, promise);
}
    @Override
 public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
     if (localAddress == null) {
         throw new NullPointerException("localAddress");
     }
     // 判断 Promise 对象是否合法。
     if (isNotValidPromise(promise, false)) {
         // cancelled
         return promise;
     }
 
     // 获得下一个 Outbound 节点
     final AbstractChannelHandlerContext next = findContextOutbound();
     // 获得下一个 Outbound 节点的执行器
     EventExecutor executor = next.executor();
     // 调用下一个 Outbound 节点的 bind 方法
     if (executor.inEventLoop()) {
         next.invokeBind(localAddress, promise);
     } else {
         safeExecute(executor, new Runnable() {
             @Override
             public void run() {
                 next.invokeBind(localAddress, promise);
             }
         }, promise, null);
     }
     return promise;
 }

The above judges whether the promise object is legal, isNotValidPromise (ChannelPromise promise, boolean allowVoidPromise)

private boolean isNotValidPromise(ChannelPromise promise, boolean allowVoidPromise) {
    if (promise == null) {
        throw new NullPointerException("promise");
    }

    // Promise 已经完成
    if (promise.isDone()) {
        // Check if the promise was cancelled and if so signal that the processing of the operation
        // should not be performed.
        //
        // See https://github.com/netty/netty/issues/2349
        if (promise.isCancelled()) {
            return true;
        }
        throw new IllegalArgumentException("promise already done: " + promise);
    }

    // Channel 不符合
    if (promise.channel() != channel()) {
        throw new IllegalArgumentException(String.format(
                "promise.channel does not match: %s (expected: %s)", promise.channel(), channel()));
    }

    // DefaultChannelPromise 合法 
    if (promise.getClass() == DefaultChannelPromise.class) {
        return false;
    }
    // 禁止 VoidChannelPromise 
    if (!allowVoidPromise && promise instanceof VoidChannelPromise) {
        throw new IllegalArgumentException(
                StringUtil.simpleClassName(VoidChannelPromise.class) + " not allowed for this operation");
    }
    // 禁止 CloseFuture
    if (promise instanceof AbstractChannel.CloseFuture) {
        throw new IllegalArgumentException(
                StringUtil.simpleClassName(AbstractChannel.CloseFuture.class) + " not allowed in a pipeline");
    }
    return false;
}

About calling the findContextOutbound () method to get the next Outbound node:
note the tail-> head from the pipeline.

private AbstractChannelHandlerContext findContextOutbound() {
    // 循环,向前获得一个 Outbound 节点
    AbstractChannelHandlerContext ctx = this;
    do {
        ctx = ctx.prev;
    } while (!ctx.outbound);
    return ctx;
}

Call the AbstractChannelHandlerContext # executor () method to obtain the executor of the next Outbound node:
If no sub-executor is set, the EventLoop of the Channel will act as the executor.

// Will be set to null if no child executor should be used, otherwise it will be set to the
// child executor.

// EventExecutor 对象
 
final EventExecutor executor;

@Override
public EventExecutor executor() {
    if (executor == null) {
        return channel().eventLoop();
    } else {
        return executor;
    }
}
  • If it is in the EventLoop thread, then call the next node's invokeBind (SocketAddress localAddress, ChannelPromise promise) method to propagate the bind event to the next node.
  • If it is not in the EventLoop thread, call safeExecute (EventExecutor executor, Runnable runnable, ChannelPromise promise, Object msg) method, and submit it to the EventLoop thread for execution:
private static void safeExecute(EventExecutor executor, Runnable runnable, ChannelPromise promise, Object msg) {
    try {
        // 提交到 EventLoop 的线程中,进行执行任务
        executor.execute(runnable);
    } catch (Throwable cause) {
        try {
            // 发生异常,回调通知 promise 相关的异常
            promise.setFailure(cause);
        } finally {
            // 释放 msg 相关的资源
            if (msg != null) {
                ReferenceCountUtil.release(msg);
            }
        }
    }
}

invokeBind(SocketAddress localAddress, ChannelPromise promise) 方法:

 private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
     if (invokeHandler()) { 
     // 判断是否符合的 ChannelHandler
     // 如果是不符合的ChannelHandler,则跳过该节点。
         try {
             // 调用该 ChannelHandler 的 bind 方法
             ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
         } catch (Throwable t) {
             notifyOutboundHandlerException(t, promise); // 通知 Outbound 事件的传播,发生异常
         }
     } else {
         // 跳过,传播 Outbound 事件给下一个节点
         bind(localAddress, promise);
     }
 }

The InvokeHandler () method is called above to determine whether it is a conforming ChannelHandler:

/**
 * Makes best possible effort to detect if {@link ChannelHandler#handlerAdded(ChannelHandlerContext)} was called
 * yet. If not return {@code false} and if called or could not detect return {@code true}.
 *
 * If this method returns {@code false} we will not invoke the {@link ChannelHandler} but just forward the event.
 * This is needed as {@link DefaultChannelPipeline} may already put the {@link ChannelHandler} in the linked-list
 * but not called {@link ChannelHandler#handlerAdded(ChannelHandlerContext)}.
 */
private boolean invokeHandler() {
    // Store in local variable to reduce volatile reads.
    int handlerState = this.handlerState;
    return handlerState == ADD_COMPLETE || (!ordered && handlerState == ADD_PENDING);
}

Here. Ordered = true nodes must be added with ChannelHandler.
For nodes with ordered = false, there is no requirement for ChannelHandler.
bind (ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) method:

@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
    unsafe.bind(localAddress, promise);
}

unsafe for processing. Explain that Unsafe is the processor of bind.


  • Inbound events are notification events.
  • The initiator of the inbound event is Unsafe, and the handler is TailContext.
  • The direction of inbound events in Pipeline is head-> tail.

Inbound and outbound are very similar, mirrored. I won't say it.

Published 46 original articles · won praise 6 · views 3847

Guess you like

Origin blog.csdn.net/weixin_43257196/article/details/104247234