Vertx Future原理解析

2,710 阅读5分钟

前言

对于使用过vert.x的开发者而言,肯定接触到过callback hell。而vert.x的Future就是解决这个问题的一种方式.

官方实例:

Future.<Message<String>>future(f ->
  vertx.eventBus().send("address1", "message", f)
).compose((msg) ->
  Future.<Message<String>>future(f ->
    vertx.eventBus().send("address2", msg.body(), f)
  )
).compose((msg) ->
  Future.<Message<String>>future(f ->
    vertx.eventBus().send("address3", msg.body(), f)
  )
).setHandler((res) -> {
  if (res.failed()) {
    //deal with exception
    return;
  }
  //deal with the result
});

本文后续分析所用实例:

public static void main(String[] args) throws InterruptedException {
    Vertx vertx=Vertx.vertx();
    //创建future
    Future<Buffer> future = Future.future();
    future.setHandler(as -> {
      if(as.succeeded()){
        System.out.println("read success");
        System.out.println(as.result().toString());
      }

    });
    String filePath = "./Downloads/abc.txt";
    FileSystem fileSystem = vertx.fileSystem();
    fileSystem.createFile(filePath, as -> {
      fileSystem.readFile(filePath, future);
    });
  }

示例调用过程分析

createFile方法

 public FileSystem createFile(String path, Handler<AsyncResult<Void>> handler) {
    createFileInternal(path, handler).run();
    return this;
  }
  
   private BlockingAction<Void> createFileInternal(String path, Handler<AsyncResult<Void>> handler) {
    return createFileInternal(path, null, handler);
  }

protected BlockingAction<Void> createFileInternal(String p, String perms, Handler<AsyncResult<Void>> handler) {
    Objects.requireNonNull(p);
    FileAttribute<?> attrs = perms == null ? null : PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(perms));
    return new BlockingAction<Void>(handler) {
      public Void perform() {
        try {
          Path target = vertx.resolveFile(p).toPath();
          if (attrs != null) {
            Files.createFile(target, attrs);
          } else {
            Files.createFile(target);
          }
        } catch (IOException e) {
          throw new FileSystemException(e);
        }
        return null;
      }
    };
  }

创建一个读取文件的动作类(实现了Handler<Future>),由于这里分析的是Future,至于具体如何createFile的这里不做分析

BlockingAction(在FileSystemImpl类中)

 protected abstract class BlockingAction<T> implements Handler<Future<T>> {

    private final Handler<AsyncResult<T>> handler;
    protected final ContextInternal context;

    public BlockingAction(Handler<AsyncResult<T>> handler) {
      this.handler = handler;
      this.context = vertx.getOrCreateContext();
    }
    /**
     * Run the blocking action using a thread from the worker pool.
     */
    public void run() {
      context.executeBlockingInternal(this, handler);
    }
    //这可以看到,他会在完成后主动调用fut的complete方法(complete具体实现会在后续分析)
    @Override
    public void handle(Future<T> fut) {
      try {
        T result = perform();
        fut.complete(result);
      } catch (Exception e) {
        fut.fail(e);
      }
    }

    public abstract T perform();

  }

BlockingAction.run方法

    public void run() {
      context.executeBlockingInternal(this, handler);
    }
    @Override
  public <T> void executeBlockingInternal(Handler<Future<T>> action, Handler<AsyncResult<T>> resultHandler) {
    executeBlocking(action, resultHandler, internalBlockingPool.executor(), internalOrderedTasks, internalBlockingPool.metrics());
  }
  
  
  <T> void executeBlocking(Handler<Future<T>> blockingCodeHandler,
      Handler<AsyncResult<T>> resultHandler,
      Executor exec, TaskQueue queue, PoolMetrics metrics) {
    Object queueMetric = metrics != null ? metrics.submitted() : null;
    try {
        //新建一个线程来执行读取文件的任务
      Runnable command = () -> {
        VertxThread current = (VertxThread) Thread.currentThread();
        Object execMetric = null;
        if (metrics != null) {
          execMetric = metrics.begin(queueMetric);
        }
        if (!DISABLE_TIMINGS) {
          current.executeStart();
        }
        //创建一个Future来接收结果(在这个Futue中调用我在Main中创建的Future)
        Future<T> res = Future.future();
        try {
          ContextImpl.setContext(this);
          //调用BlockingAction的handler
          blockingCodeHandler.handle(res);
        } catch (Throwable e) {
          res.tryFail(e);
        } finally {
          if (!DISABLE_TIMINGS) {
            current.executeEnd();
          }
        }
        if (metrics != null) {
          metrics.end(execMetric, res.succeeded());
        }
        //这就是上边可以调用到我创建future的原因
        if (resultHandler != null) {
          res.setHandler(ar -> runOnContext(v -> resultHandler.handle(ar)));
        }
      };
      //在vertx中执行这个Runnable,这里如何实现的异步,就涉及到了vertx的线程模型以及netty的原理分析,超过了这篇文章的范围,不再分析
      if (queue != null) {
        queue.execute(command, exec);
      } else {
        exec.execute(command);
      }
    } catch (RejectedExecutionException e) {
      // Pool is already shut down
      if (metrics != null) {
        metrics.rejected(queueMetric);
      }
      throw e;
    }
  }
    

这就是这个示例整个的流程,下面就分析下Future的具体实现

Future实现解析

Future继承了AsyncResult接口,所以future本身可以携带异步结果的同时也可以是AsyncResult异步结果的携带者。

Future也继承了Handler<AsyncResult>,所以很多调用处需要Handler的地方,也可以直接传Future

Vert.x本身最常用的实现类是FutureImpl,所以后续主要分析的就是FutureImpl(至于如何使用FutureFactory创建的,感兴趣的请自行查看源码)

FutureImpl中的变量:

  • failed,true:这是失败的异步结果。此时throwable不为空。
  • succeeded,true:这是成功的异步结果。如果有设置结果的话,result不为空。
  • handler:真正的异步结果处理器,setHandler方法传入的handler保存在这里。
  • result:结果属性。
  • throwable:异常结果属性

complete/tryComplete方法

 @Override
  public void complete(T result) {
    if (!tryComplete(result)) {
      throw new IllegalStateException("Result is already complete: " + (succeeded ? "succeeded" : "failed"));
    }
  }
  
  @Override
  public boolean tryComplete(T result) {
    Handler<AsyncResult<T>> h;
    synchronized (this) {
      if (succeeded || failed) {
        return false;
      }
      this.result = result;
      succeeded = true;
      h = handler;
      handler = null;
    }
    if (h != null) {
      h.handle(this);
    }
    return true;
  }

complete方法委托给tryComplete方法来实现。如果tryComplete方法返回false,则说明此异步结果已经完成了,进入if分支,抛出异常。由此可以看出future的最终状态确定下来,就不再改变。

接下来看tryComplete方法。很简单。就是把结果赋值给result。设置到succeeded为true代表当前AsyncResult已经完成。如果当前已经设置了handler那么就触发该handler。(大多数情况,这是已经设置到handler)。failed方法此时类似。

setHandler方法

 /**
   * Set a handler for the result. It will get called when it's complete
   */
  public Future<T> setHandler(Handler<AsyncResult<T>> handler) {
    boolean callHandler;
    synchronized (this) {
      callHandler = isComplete();
      if (!callHandler) {
        this.handler = handler;
      }
    }
    if (callHandler) {
      handler.handle(this);
    }
    return this;
  }
  
  public synchronized boolean isComplete() {
    return failed || succeeded;
  }

sethandler方法也很简单,就是把handler赋值给属性handler。如果当前future的AsyncResult已经完成,那么触发handler。

handle方法

public void handle(AsyncResult<T> asyncResult) {
    if (asyncResult.succeeded()) {
      complete(asyncResult.result());
    } else {
      fail(asyncResult.cause());
    }
  }

很简单,就是调用complete方法和failed方法即可。

compose方法

default <U> Future<U> compose(Function<T, Future<U>> mapper) {
    if (mapper == null) {
      throw new NullPointerException();
    }
    //先创建好future,然后下面返回这个新创建的future。
    Future<U> ret = Future.future();
   //给当前future设置handler。这个handler在将来某个时刻执行。
    setHandler(ar -> {
      //这个代码块的代码跟外面的代码执行时机不同。
      //1. 外面的ret,将在当前future的compose方法调用时执行并返回。
      //2. 在下一个compose方法或者setHandler方法为ret设置handler。

      // 将来某一时刻到了。执行了这个代码块。 到了此时,当前future已经完成,
      //并且执行handler中的handle方法。
      if (ar.succeeded()) {
        Future<U> apply;
        try {
        //3. 获取future中的异步结果。调用compose方法的传入mapper这个functionalInterface
        //4.mapper返回下一个异步操作future(apply)。
          apply = mapper.apply(ar.result());
        } catch (Throwable e) {
       //执行mapper失败,直接设置ret的异常结果。以完成ret这个future,且触发handler的执行
          ret.fail(e);
          return;
        }
       //5.为apply设置handler。(此时的ret已经完成handler的设置,执行时机不同)
       //6. 这个apply future又会将在未来某一时刻执行。注意:在未来某一时刻。执行时机不同。
       //7. 当apply完成时,调用handler。apply自身作为异步结果(AsyncResult),
       //调用ret(此时ret作为Handler)
        apply.setHandler(ret);
      } else {
       //如果当前的函数是失败状态的,直接设置ret的异常结果。
       //以完成ret这个future,且触发handler的执行
        ret.fail(ar.cause());
      }
    });
    return ret;
  }

map方法和compose方法类似(更简单),这里就不进行分析了