Future-Listener机制

70 阅读5分钟

为什么出现Future

Thread.png

Thread实现Runnable,它无法返回子线程执行结果。Callable结合Future返回执行结果,实现伪异步:call执行结束,子线程会立即返回一个Future给父线程,父线程不用等待子线程执行,可以继续做自己的事,过段时间调用future.get()获取结果,如果子线程执行结束,可以获取到结果,但如果没有结束,父线程要阻塞等待。一般轮询isDone()来判断,然后再调用get方法。

如何实现netty的被观察者

父线程阻塞,等待子线程完成来唤醒?那不又回到阻塞了么。唤醒机制适用于线程间的同步,特别是在需要等待某个条件或资源时使用。这里我们要实现异步,需要子线程完成后主动调用本来需要父线程要执行的逻辑。父线程"盯的"是子线程返回的结果Future对象,抛开线程,那我们关注的是Future这个对象,将它作为Subject主题的实现,那么这个Future应该具备管理观察者列表,唤醒观察者。

netty的FutureListener机制.iodraw.png

ChannelFuture整合Future和通道

public interface ChannelFuture extends Future<Void> {

Channel channel();

@Override
ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> listener);

@Override
ChannelFuture addListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);

@Override
ChannelFuture removeListener(GenericFutureListener<? extends Future<? super Void>> listener);

}

DefaultChannelPromise,整合通道和Future以及promise

手动控制异步完成时机。

public class DefaultChannelPromise extends DefaultPromise<Void> implements ChannelPromise, FlushCheckpoint {

private final Channel channel;

@Override
protected EventExecutor executor() {
    EventExecutor e = super.executor();
    if (e == null) {
        return channel().eventLoop();
    } else {
        return e;
    }
}
//手动控制异步操作的完成时机时
@Override
public ChannelPromise setSuccess(Void result) {
    super.setSuccess(result);
    return this;
}

}

到此被观察者已经可以管理观察者列表(add,remove),还能唤醒执行观察者的应对事件发生的逻辑

Obsrve->Listener

通道读结束之后,关闭当前通道返回一个ChannelFutrue,此时将关闭父通道的逻辑注册到ChannelFutrue上,我们不用等待通道关闭返回结果。通道读写就结束了。

注册.iodraw.png

public interface EventListener {
}
public interface GenericFutureListener<F extends Future<?>> extends EventListener {
    void operationComplete(F future) throws Exception;
}
//源码测试中的例子,在请求关闭通道时,会注册关闭父通道的监听
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf buffer = (ByteBuf) msg;
    received += buffer.readableBytes();
    buffer.release();
    if (received == bufferSize) {//ctx.close()关闭通道,返回所关闭的通道实例
        ctx.close().addListener((ChannelFuture future) -> {
            // Close the underlying QuicChannel as well.
            future.channel().parent().close();
        });
    }
}

监听触发

当前线程是EventLoop线程 → 直接同步执行关闭操作

如果不是 EventLoop 线程 → 提交任务到线程池,异步执行关闭操作

4.1虽然是同步执行的关闭,该机制仍属于 Netty 异步非阻塞模型的一部分,因为它通过 Promise 监听器解耦了操作的触发和结果处理,确保了线程安全和高效执行。

public ChannelFuture close(final ChannelPromise promise) {
    // 1. 检查 Promise 有效性
    if (isNotValidPromise(promise, false)) {
        // cancelled
        return promise;
    }
   // 2. 找到下一个 Outbound 处理器上下文(支持关闭操作)
    final AbstractChannelHandlerContext next = findContextOutbound(MASK_CLOSE);
    // 3. 获取关联的 EventLoop(线程池)
    EventExecutor executor = next.executor();
    // 4. 判断当前线程是否是 EventLoop 线程
    if (executor.inEventLoop()) {
    // 4.1 如果是EventLoop线程 → 直接同步执行关闭操作
        next.invokeClose(promise);
    } else {
    // 4.2 如果不是EventLoop线程 → 提交任务到线程池,异步执行关闭操作
        safeExecute(executor, new Runnable() {
            @Override
            public void run() {
                next.invokeClose(promise);
            }
        }, promise, null, false);
    }

    return promise;
}
// 通道关闭的核心实现,处理关闭流程的各个阶段
protected void close(final ChannelPromise promise, // 用于通知关闭结果的Promise对象
                    final Throwable cause,         // 导致关闭的异常(用于失败刷新消息)
                    final ClosedChannelException closeCause) { // 通道关闭专用异常

    // 1. 设置Promise不可取消,确保关闭流程必须执行完成
    if (!promise.setUncancellable()) {
        return; // 如果已经被取消,直接返回
    }

    // 2. 处理重复关闭请求(幂等性处理)
    if (closeInitiated) {
        if (closeFuture.isDone()) {
            // 如果已经关闭完成,直接设置Promise成功
            safeSetSuccess(promise);
        } else if (!(promise instanceof VoidChannelPromise)) {
            // 非空Promise需要等待现有关闭操作完成
            closeFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) {
                    promise.setSuccess(); // 继承现有关闭操作的结果
                }
            });
        }
        return;
    }

    // 3. 标记关闭流程开始(原子操作)
    closeInitiated = true;

    // 4. 保存通道状态和缓冲区引用
    final boolean wasActive = isActive(); // 记录关闭前的活跃状态
    final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer; // 获取待发送消息缓冲区
    this.outboundBuffer = null; // 禁止继续写入新消息(防止并发操作)

    // 5. 准备关闭执行器(某些传输层需要特殊线程执行关闭)
    Executor closeExecutor = prepareToClose();
    if (closeExecutor != null) {
        // 6. 需要特殊执行器的情况(如NIO需要Selector线程)
        closeExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    // 7. 执行底层关闭操作(如Socket.close())
                    doClose0(promise);
                } finally {
                    // 8. 确保后续操作回到EventLoop线程
                    invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            // 9. 处理未发送消息(重要:失败排队中的消息)
                            if (outboundBuffer != null) {
                                outboundBuffer.failFlushed(cause, false); // 标记已flush的消息失败
                                outboundBuffer.close(closeCause);         // 关闭缓冲区并释放资源
                            }
                            // 10. 触发通道非激活事件并注销
                            fireChannelInactiveAndDeregister(wasActive);
                        }
                    });
                }
            }
        });
    } else {
        // 11. 普通关闭流程(当前线程执行)
        try {
            doClose0(promise); // 直接执行底层关闭
        } finally {
            // 12. 无论是否异常都要处理缓冲区
            if (outboundBuffer != null) {
                outboundBuffer.failFlushed(cause, false);
                outboundBuffer.close(closeCause);
            }
        }

        // 13. 处理flush过程中的特殊状态
        if (inFlush0) { // 如果当前正在执行flush操作
            // 延迟触发事件到flush完成后
            invokeLater(new Runnable() {
                @Override
                public void run() {
                    fireChannelInactiveAndDeregister(wasActive);
                }
            });
        } else {
            // 立即触发通道状态变更
            fireChannelInactiveAndDeregister(wasActive);
        }
    }
}

// 实际的底层关闭操作封装
private void doClose0(ChannelPromise promise) {
    try {
        doClose(); // 抽象方法,由具体传输实现(如NioSocketChannel)
        closeFuture.setClosed(); // 设置关闭Future状态
        safeSetSuccess(promise); // 通知关闭成功
    } catch (Throwable t) {
        // 异常处理:记录日志并通知Promise
        closeFuture.setClosed();
        safeSetFailure(promise, t); // 传播异常给调用方
    }
}

总结

  1. netty中IO操作(bind,read,write,connect等)会立即返回ChannelFuture
  2. 向ChannelFuture中注册监听(GenericFutureListener),IO执行结束回调它的operationComplete方法