- 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);
}
- This method is implemented from the ChannelOutboundInvoker interface.
- 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.