netty-Future、Promise和Listener源码分析

文章目录

  • 1 netty Future的基本使用
  • 2 netty Future实现的原理
    • 2.1 netty Future的继承关系
    • 2.2 DefaultChannelPromise源码分析
      • 2.2.1 DefaultChannelPromise的创建
      • 2.2.2 addListener源码分析
      • 2.2.3 sync/await阻塞等待分析
      • 2.2.4 设置future结果的源码分析
      • 2.2.5 获取future操作结果的源码分析
      • 2.2.6 future取消--cancel方法源码分析
  • 3 netty Future在netty中几个典型的应用场景
    • 3.1 异步写操作

1 netty Future的基本使用

编程语言中的future,顾名思义就是一个操作在将来才会完成,但是调用方不必同步等待结果,只要持有对应操作的future对象即可,等操作完成了,从future中获取结果。

netty Future是实现其异步非阻塞IO机制的重要对象。本文从演示netty Future的基本使用开始,再到源码角度分析其实现原理,最后剖析其在netty的异步非阻塞IO中如何使用。

netty Future在jdk Future的基础上进行了扩展,最主要的特色是提供了Listener机制,在future完成后回调listner执行用户指定的操作。其他特性主要是提供sync()方法,以等待future完成后再继续往后做其他事情。(如无特别说明,本文后续所说的future都特指netty future。)

  • future的基本使用方式举例:
public class TimeServerHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		final ChannelFuture cf = ctx.writeAndFlush(new UnixTime());
		cf.addListener(new ChannelFutureListener() {
			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				assert cf == future;
				ctx.close();
			}
		});
	}
}
  • future只有两种状态:未完成与完成,完成后需要用户自己判断future对应的操作最后是成功、失败还是取消:
 *                                      +---------------------------+
 *                                      | Completed successfully    |
 *                                      +---------------------------+
 *                                 +---->      isDone() = true      |
 * +--------------------------+    |    |   isSuccess() = true      |
 * |        Uncompleted       |    |    +===========================+
 * +--------------------------+    |    | Completed with failure    |
 * |      isDone() = false    |    |    +---------------------------+
 * |   isSuccess() = false    |----+---->      isDone() = true      |
 * | isCancelled() = false    |    |    |       cause() = non-null  |
 * |       cause() = null     |    |    +===========================+
 * +--------------------------+    |    | Completed by cancellation |
 *                                 |    +---------------------------+
 *                                 +---->      isDone() = true      |
 *                                      | isCancelled() = true      |
 *                                      +---------------------------+

2 netty Future实现的原理

2.1 netty Future的继承关系

netty-Future、Promise和Listener源码分析_第1张图片

  • 最上面是jdk Future,netty Future继承jdk Future,主要添加了以下几个接口方法:

— boolean isSuccess():判断操作是否成功;

— addListener/removeListener:用于添加删除监听器;

— sync/await:用于阻塞等待Future完成,当sync/await方法返回后,future完成。

  • Promise继承自Future,主要添加了如下几个方法,用于设置Future的结果:

— setSuccess(V result) / trySuccess(V result):设置future结果为成功;

— setFailure(Throwable cause) / tryFailure(Throwable cause):设置future结果为失败。

  • ChannelFuture继承自Future,添加了Channel channel()方法,用于获取执行IO操作的channel。
  • ChannelPromise基本是把Promise接口和ChannelFuture接口合并到一起的接口。

以上是整个Future类体系的概要,而在netty中,经常用到的是DefaultChannelPromise,下面从DefaultChannelPromise开始,分析netty future的源码实现。

2.2 DefaultChannelPromise源码分析

netty future的一般使用流程是:创建—>调用addListener添加监听器—>调用sync/await进行同步等待—>调用trySuccess/tryFailure设置future结果—>调用get获取future结果。下面,就按照这个顺序,分析源码。

2.2.1 DefaultChannelPromise的创建

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

    private final Channel channel;

    public DefaultChannelPromise(Channel channel) {
        this.channel = checkNotNull(channel, "channel");
    }

    public DefaultChannelPromise(Channel channel, EventExecutor executor) {
        super(executor);
        this.channel = checkNotNull(channel, "channel");
    }
}  

从源码可知DefaultChannelPromise主要属性是一个Channel。

第二个构造器还支持传入一个EventExecutor,该executor传到其父类DefaultPromise,在通知listener时用于执行通知listener的任务。同时从extends DefaultPromise可知,使用DefaultChannelPromise的场景,future操作的结果是个Void对象,没有业务数据,只能表示结果是成功还是失败。

来看看父类DefaultPromise的主要属性和构造器:

public class DefaultPromise<V> extends AbstractFuture<V> implements Promise<V> {

    // 实例变量
    private volatile Object result;				// future操作的结果
    private final EventExecutor executor;	// 通知listener的执行器,通过exector在其他线程执行
    private Object listeners;						  // 监听器
    private short waiters;							  // 因调用sync/await/get方法而等待该future结果的线程数量
    private boolean notifyingListeners;	  // 是否正在通知监听器,在准备通知listener之前设置为true

    public DefaultPromise(EventExecutor executor) {
        this.executor = checkNotNull(executor, "executor");
    }

    protected DefaultPromise() {
        // only for subclasses
        executor = null;
    }
}

源码的解释见注释,值得关注的是DefaultPromise的构造器,无参构造器只能由子类调用;而修饰符为public的构造器,则必须传入一个EventExecutor,所以如果在子类以外的地方使用DefaultPromise,必须传入单独的EventExecutor。

2.2.2 addListener源码分析

从上文可知DefaultPromise中监听器的属性类型是个Object:

private Object listeners;						  // 监听器

netty这么做是为了支持多个场景下的listener,大多数情况下我们赋值给listeners属性的是单个listener。但是有些场景下会添加多个监听器,这时赋值给listeners属性的是一个DefaultFutureListeners,它是listener对象的容器,由于后文分析的netty相关的场景中,没有用到DefaultFutureListeners,所以DefaultFutureListeners按下不表。

添加监听器有两个方法:

–addListener(GenericFutureListener> listener):添加单个listener;

–addListeners(GenericFutureListener>… listeners):添加多个listener。

因为他们最后都调用addListener0来真正完成添加listener的操作,它们逻辑大体相同,所以只说前面的单个的addListener方法:

public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
    checkNotNull(listener, "listener");

    synchronized (this) {
        addListener0(listener);
    }

    if (isDone()) {
        notifyListeners();
    }

    return this;
}

解读:添加监听器需要保证同步,添加监听器之后,如果该future已经完成,则直接调用notifyListeners()通知监听器,通知的方法后面再展开,这里先来看addListener0:

private void addListener0(GenericFutureListener<? extends Future<? super V>> listener) {
    if (listeners == null) {
        listeners = listener;
    } else if (listeners instanceof DefaultFutureListeners) {
        ((DefaultFutureListeners) listeners).add(listener);
    } else {
        listeners = new DefaultFutureListeners((GenericFutureListener<?>) listeners, listener);
    }
}

2.2.3 sync/await阻塞等待分析

sync()方法源码,在DefaultPromise中:

public Promise<V> sync() throws InterruptedException {
    await();						// 2.2.3-1
    rethrowIfFailed();	// 2.2.3-2
    return this;
}
  • 2.2.3-1 await():
public Promise<V> await() throws InterruptedException {
    if (isDone()) {
        return this;  // (1)
    }

    if (Thread.interrupted()) {
        throw new InterruptedException(toString());
    }

    checkDeadLock();  // (2)

    synchronized (this) {
        while (!isDone()) {
            incWaiters();
            try {
                wait();  // (3)
            } finally {
                decWaiters();
            }
        }
    }
    return this;
}

主逻辑是:(1)-先判断如果future已经完成,则不用等待,直接返回;

(2)-否则检查死锁,这里检查死锁的逻辑是调用await的线程和IO线程不能是同一个线程,否则将会导致死锁。因为future的状态就是由IO线程更新的,如果IO线程挂起了,就没谁来更新future了,于是就一直wait。这种死锁属于自己把自己挂起,跟通常说的两个线程互相等待资源的死锁不一样。

(3)-如果以上都没问题,则调用该future对象的wait()方法无限等待,直到别的线程调用notify()/notifyAll()方法。在进入等待前,将等待线程计数器+1,finally块在等待回复之后将等待线程数-1。

  • 2.2.3-2 rethrowIfFailed()只是将异常进行抛出,不展开说明。

2.2.4 设置future结果的源码分析

当操作完成后,如果成功则通过trySuccess/setSuccess设置成功结果,如果失败则通过tryFailure/setFailure设置失败原因。设置成功结果调用过程如下:

/*=================DefaultChannelPromise==================*/
public ChannelPromise setSuccess(Void result) {
    super.setSuccess(result);
    return this;
}

/*=================DefaultPromise==================*/
public Promise<V> setSuccess(V result) {
    if (setSuccess0(result)) {
        return this;
    }
    throw new IllegalStateException("complete already: " + this);
}

private boolean setSuccess0(V result) {
    return setValue0(result == null ? SUCCESS : result);
}

private boolean setValue0(Object objResult) {
    if (RESULT_UPDATER.compareAndSet(this, null, objResult) ||
        RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) {
        if (checkNotifyWaiters()) {  // 2.2.4-1
            notifyListeners();   // 2.2.4-2
        }
        return true;
    }
    return false;
} 

通过RESULT_UPDATER执行result实例变量的原子更新操作,如果result档期是null或者UNCANCELLABLE,则更新为方法参数指定的操作结果。调用方调用future.setUncancellable()时,会设置result为UNCANCELLABLE。

结果设置成功后,调用checkNotifyWaiters()将因为调用sync()/await()阻塞等待的线程唤醒,调用notifyListeners()通知之前注册的监听器。

  • 2.2.4-1 checkNotifyWaiters()的逻辑:
private synchronized boolean checkNotifyWaiters() {
    if (waiters > 0) {
        notifyAll();
    }
    return listeners != null;
} 
  • 2.2.4-2 notifyListeners()逻辑
private void notifyListeners() {
    EventExecutor executor = executor();
    if (executor.inEventLoop()) {
        final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
        final int stackDepth = threadLocals.futureListenerStackDepth();
        if (stackDepth < MAX_LISTENER_STACK_DEPTH) {
            threadLocals.setFutureListenerStackDepth(stackDepth + 1);
            try {
                notifyListenersNow();	// 2.2.4-3
            } finally {
                threadLocals.setFutureListenerStackDepth(stackDepth);
            }
            return;
        }
    }

    safeExecute(executor, new Runnable() {
        @Override
        public void run() {
            notifyListenersNow();  // 2.2.4-3
        }
    });
}

notifyListeners()逻辑主要分两大块:

(1) 先判断调用当前future方法的是否是IO线程(即eventLoop线程)。如果是,则取出当前的listener调用栈深度stackDepth,stackDepth如果小于允许的最大栈深度,则继续执行。将栈深度+1,然后通知所有listeners,通知完后再将栈深度设置回之前的值,然后返回。stackDepth稍后解释。

(2) 如果调用future方法的不是IO线程,或者虽然是IO线程但是栈深度超过了允许的最大栈深度,那么将通知listeners封装为一个单独的任务,由executor执行。

  • 2.2.4-3 notifyListenersNow()源码:
private void notifyListenersNow() {
    Object listeners;
    synchronized (this) {
        if (notifyingListeners || this.listeners == null) {
            return;
        }
        notifyingListeners = true;
        listeners = this.listeners;
        this.listeners = null;
    }
    for (;;) {
        if (listeners instanceof DefaultFutureListeners) {
            notifyListeners0((DefaultFutureListeners) listeners);
        } else {
            notifyListener0(this, (GenericFutureListener<?>) listeners);  // 2.2.4-4
        }
        synchronized (this) {
            if (this.listeners == null) {
                notifyingListeners = false;
                return;
            }
            listeners = this.listeners;
            this.listeners = null;
        }
    }
}

源码解读:(1) 先判断如果还有listeners未通知,则设置状态为通知中,并取出listeners放到局部变量,将示例变量this.listeners置空。

(2) 循环执行,首先通知listeners,然后再判断局部变量this.listeners是否还有监听器,因为notifyListener0方法没有加锁,在通知期间可能还有listener加入。不断循环指导没有listener为止。

private static void notifyListener0(Future future, GenericFutureListener l) {
    try {
        l.operationComplete(future);
    } catch (Throwable t) {
        if (logger.isWarnEnabled()) {
            logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", t);
        }
    }
}

非常简单,直接调用listener的operationComplete方法,并以future作为参数。

  • stackDepth的解释

以当前future为参数调用listener.operationComplete(future),可以为后续处理带来方便,比如从channelFuture中取出channel,做与channel相关的操作。但是,也无法阻止用户在operationComplete中写如下代码:

future.addListener(listener);

也就是在listener收到future完成的通知后,又添加listener。根据上文对addListener的分析,可知这个调用后续又继续会调用到nofifyListenersNow()方法,也即nofifyListenersNow()发生了递归调用。因为设置future结束状态一般是IO线程发起,如果递归调用层次太深,会严重影响IO效率。因此,引入了最大栈深度MAX_LISTENER_STACK_DEPTH和当前递归深度stackDepth,来解决这个问题。如果stackDepth>=MAX_LISTENER_STACK_DEPTH,则把通知listener的操作封装为一个任务,由executor另外执行。

2.2.5 获取future操作结果的源码分析

  • 可以通过getNow()非阻塞地获取future结果,如果result有实际业务意义,将返回result,否则返回null,代码如下,可知如果future未完成,返回的也是null:
public V getNow() {
    Object result = this.result;
    if (result instanceof CauseHolder || result == SUCCESS || result == UNCANCELLABLE) {
        return null;
    }
    return (V) result;
}
/*========================AbstractFuture=========================*/
public V get() throws InterruptedException, ExecutionException {
    await();

    Throwable cause = cause();
    if (cause == null) {
        return getNow();
    }
    if (cause instanceof CancellationException) {
        throw (CancellationException) cause;
    }
    throw new ExecutionException(cause);
}

2.2.6 future取消–cancel方法源码分析

最后再看看取消方法:

public boolean cancel(boolean mayInterruptIfRunning) {
    if (RESULT_UPDATER.compareAndSet(this, null, CANCELLATION_CAUSE_HOLDER)) {
        if (checkNotifyWaiters()) {
            notifyListeners();
        }
        return true;
    }
    return false;
}

解读:如果result还是null,则将result设置为CauseHolder对象,唤醒等待的线程,最后调用notifyListeners()方法通知监听器。然后返回true表示取消成功。

如果result非空了,则返回false表示取消失败。

3 netty Future在netty中几个典型的应用场景

3.1 异步写操作

通常我们会调用ChannelHandlerContext.writeAndFlush(Object msg)方法写出数据到网络:

final ChannelFuture cf = ctx.writeAndFlush(new UnixTime());
cf.addListener(new ChannelFutureListener() {
	@Override
	public void operationComplete(ChannelFuture future) throws Exception {
		assert cf == future;
		System.out.println("write completed !!!")
	}
});

下面分析netty future在其中的使用。writeAndFlush(Object msg)方法的一个实现在AbstractChannelHandlerContext:

public ChannelFuture writeAndFlush(Object msg) {
    return writeAndFlush(msg, newPromise());
}

这里新建一个future,然后继续往后调用重载的writeAndFlush方法。看看newPromise()如何创建一个future:

public ChannelPromise newPromise() {
    return new DefaultChannelPromise(channel(), executor());
}

继续往后看writeAndFlush(msg, executor):

public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
    write(msg, true, promise);
    return promise;
}

最后msg会被缓存在AbstractUnsafe的ChannelOutboundBuffer中,ChannelOutboundBuffer用一个链表来保存待发送的消息,每个链表节点是个Entry,Entry的数据结构如下:

static final class Entry {
    private static final Recycler<Entry> RECYCLER = new Recycler<Entry>() {
        @Override
        protected Entry newObject(Handle<Entry> handle) {
            return new Entry(handle);
        }
    };

    private final Handle<Entry> handle;
    Entry next;
    Object msg;
    ByteBuffer[] bufs;
    ByteBuffer buf;
    ChannelPromise promise;
    long progress;
    long total;
    int pendingSize;
    int count = -1;
    boolean cancelled;
}

可见msg消息和对应的promise都被放在Entry对象中,后面调用flush方法时,会真正调用channel写方法,并在那里面会设置promise(也就是前文说的future)的结果:

/*=======================AbstractNioByteChannel=======================*/
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
    int writeSpinCount = config().getWriteSpinCount();
    do {
        Object msg = in.current();
        if (msg == null) {
            // Wrote all messages.
            clearOpWrite();
            // Directly return here so incompleteWrite(...) is not called.
            return;
        }
        writeSpinCount -= doWriteInternal(in, msg);
    } while (writeSpinCount > 0);

    incompleteWrite(writeSpinCount < 0);
}

private int doWriteInternal(ChannelOutboundBuffer in, Object msg) throws Exception {
    // 省略其他代码
    in.remove();
    // 省略其他代码
}

/*==========================ChannelOutboundBuffer========================*/
public boolean remove() {
  // 省略其他代码	
	ChannelPromise promise = e.promise;
  // 省略其他代码
	safeSuccess(promise);
  // 省略其他代码
}

private static void safeSuccess(ChannelPromise promise) {
    PromiseNotificationUtil.trySuccess(promise, null, promise instanceof VoidChannelPromise ? null : logger);
}

/*==========================PromiseNotificationUtil========================*/
public static <V> void trySuccess(Promise<? super V> p, V result, InternalLogger logger) {
    if (!p.trySuccess(result) && logger != null) {
        Throwable err = p.cause();
        if (err == null) {
            logger.warn("Failed to mark a promise as success because it has succeeded already: {}", p);
        } else {
            logger.warn(
                    "Failed to mark a promise as success because it has failed already: {}, unnotified cause:",
                    p, err);
        }
    }
}

好了,全篇分析到此结束,写作过程中努力尽量使文章有条理,但是源码分析,感觉还是写得稍微繁杂。


各个场景使用的具体promise:

场景 使用的具体Future类 Future的结果
writeAndFlush DefaultChannelPromise SUCCESS,是个java.lang.Object类的实例
bind DefaultChannelPromise SUCCESS,是个java.lang.Object类的实例
connect DefaultChannelPromise SUCCESS,是个java.lang.Object类的实例

你可能感兴趣的:(netty)