编程语言中的future,顾名思义就是一个操作在将来才会完成,但是调用方不必同步等待结果,只要持有对应操作的future对象即可,等操作完成了,从future中获取结果。
netty Future是实现其异步非阻塞IO机制的重要对象。本文从演示netty Future的基本使用开始,再到源码角度分析其实现原理,最后剖析其在netty的异步非阻塞IO中如何使用。
netty Future在jdk Future的基础上进行了扩展,最主要的特色是提供了Listener机制,在future完成后回调listner执行用户指定的操作。其他特性主要是提供sync()方法,以等待future完成后再继续往后做其他事情。(如无特别说明,本文后续所说的future都特指netty 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();
}
});
}
}
* +---------------------------+
* | 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 |
* +---------------------------+
— boolean isSuccess():判断操作是否成功;
— addListener/removeListener:用于添加删除监听器;
— sync/await:用于阻塞等待Future完成,当sync/await方法返回后,future完成。
— setSuccess(V result) / trySuccess(V result):设置future结果为成功;
— setFailure(Throwable cause) / tryFailure(Throwable cause):设置future结果为失败。
以上是整个Future类体系的概要,而在netty中,经常用到的是DefaultChannelPromise,下面从DefaultChannelPromise开始,分析netty future的源码实现。
netty future的一般使用流程是:创建—>调用addListener添加监听器—>调用sync/await进行同步等待—>调用trySuccess/tryFailure设置future结果—>调用get获取future结果。下面,就按照这个顺序,分析源码。
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
来看看父类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。
从上文可知DefaultPromise中监听器的属性类型是个Object:
private Object listeners; // 监听器
netty这么做是为了支持多个场景下的listener,大多数情况下我们赋值给listeners属性的是单个listener。但是有些场景下会添加多个监听器,这时赋值给listeners属性的是一个DefaultFutureListeners,它是listener对象的容器,由于后文分析的netty相关的场景中,没有用到DefaultFutureListeners,所以DefaultFutureListeners按下不表。
添加监听器有两个方法:
–addListener(GenericFutureListener extends Future super V>> listener):添加单个listener;
–addListeners(GenericFutureListener extends Future super V>>… 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);
}
}
sync()方法源码,在DefaultPromise中:
public Promise<V> sync() throws InterruptedException {
await(); // 2.2.3-1
rethrowIfFailed(); // 2.2.3-2
return this;
}
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。
当操作完成后,如果成功则通过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()通知之前注册的监听器。
private synchronized boolean checkNotifyWaiters() {
if (waiters > 0) {
notifyAll();
}
return listeners != null;
}
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执行。
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作为参数。
以当前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另外执行。
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);
}
最后再看看取消方法:
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表示取消失败。
通常我们会调用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类的实例 |