官方文档:

  1. https://gitee.com/jd-platform-opensource/asyncTool/blob/master/QuickStart.md

场景

之前做项目的时候面临一个场景:一个业务操作有很多异步任务,任务与任务之间又有关联,如下图的例子:

  1. 多个执行单元的串行请求
    asyncTool框架 - 图1

  2. 多个执行单元的并行请求
    asyncTool框架 - 图2

  3. 阻塞等待,串行的后面跟多个并行
    asyncTool框架 - 图3

  4. 阻塞等待,多个并行的执行完毕后才执行某个
    asyncTool框架 - 图4

  5. 串并行相互依赖
    asyncTool框架 - 图5

  6. 更复杂的场景
    asyncTool框架 - 图6

对于上面的场景我尝试了多种方式:

  1. JDK8的CompleteFuture
    2. Reactive
    3. Future

传统的Future、CompleteableFuture一定程度上可以完成任务编排,并可以把结果传递到下一个任务。如CompletableFuture有then方法,但是却无法做到对每一个执行单元的回调。譬如A执行完毕成功了,后面是B,我希望A在执行完后就有个回调结果,方便我监控当前的执行状况,或者打个日志什么的。失败了,我也可以记录个异常信息什么的。
此时,CompleteableFuture就无能为力了。
AsyncTool框架提供了这样的回调功能。并且,如果执行异常、超时,可以在定义这个执行单元时就设定默认值

我用了asyncTool,任务与任务的关系变简单了,我能随意编排任务。

实战演示

A执行完毕后,开启另外两个B和C,另外两个执行完毕后,开始执行F
asyncTool框架 - 图7

引入maven依赖

  1. <dependency>
  2. <groupId>com.gitee.jd-platform-opensource</groupId>
  3. <artifactId>asyncTool</artifactId>
  4. <version>V1.4-SNAPSHOT</version>
  5. </dependency>

基本组件

worker: 一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。
T,V两个泛型,分别是入参和出参类型。
譬如该耗时操作,入参是String,执行完毕的结果是Integer,那么就可以用泛型来定义。
多个不同的worker之间,没有关联,分别可以有不同的入参、出参类型。

  1. /**
  2. * 每个最小执行单元需要实现该接口
  3. * @author wuweifeng wrote on 2019-11-19.
  4. */
  5. public interface IWorker<T, V> {
  6. /**
  7. * 在这里做耗时操作,如rpc请求、IO等
  8. *
  9. * @param object
  10. * object
  11. */
  12. V action(T object, Map<String, WorkerWrapper> allWrappers);
  13. /**
  14. * 超时、异常时,返回的默认值
  15. * @return 默认值
  16. */
  17. V defaultValue();
  18. }

callBack:对每个worker的回调。worker执行完毕后,会回调该接口,带着执行成功、失败、原始入参、和详细的结果。

  1. /**
  2. * 每个执行单元执行完毕后,会回调该接口</p>
  3. * 需要监听执行结果的,实现该接口即可
  4. * @author wuweifeng wrote on 2019-11-19.
  5. */
  6. public interface ICallback<T, V> {
  7. void begin();
  8. /**
  9. * 耗时操作执行完毕后,就给value注入值
  10. *
  11. */
  12. void result(boolean success, T param, WorkResult<V> workResult);
  13. }

wrapper:组合了worker和callback,是一个 最小的调度单元 。通过编排wrapper之间的关系,达到组合各个worker顺序的目的。
wrapper的泛型和worker的一样,决定了入参和结果的类型。

  1. WorkerWrapper<String, String> workerWrapper = new WorkerWrapper<>(w, "0", w);
  2. WorkerWrapper<String, String> workerWrapper1 = new WorkerWrapper<>(w1, "1", w1);
  3. WorkerWrapper<String, String> workerWrapper2 = new WorkerWrapper<>(w2, "2", w2);
  4. WorkerWrapper<String, String> workerWrapper3 = new WorkerWrapper<>(w3, "3", w3);


image.png
0执行完,同时1和2, 1\2都完成后3。3会等待2完成
此时,你可以定义一个 worker

  1. /**
  2. * @author wuweifeng wrote on 2019-11-20.
  3. */
  4. public class ParWorker1 implements IWorker<String, String>, ICallback<String, String> {
  5. @Override
  6. public String action(String object) {
  7. try {
  8. Thread.sleep(1000);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. return "result = " + SystemClock.now() + "---param = " + object + " from 1";
  13. }
  14. @Override
  15. public String defaultValue() {
  16. return "worker1--default";
  17. }
  18. @Override
  19. public void begin() {
  20. //System.out.println(Thread.currentThread().getName() + "- start --" + System.currentTimeMillis());
  21. }
  22. @Override
  23. public void result(boolean success, String param, WorkResult<String> workResult) {
  24. if (success) {
  25. System.out.println("callback worker1 success--" + SystemClock.now() + "----" + workResult.getResult()
  26. + "-threadName:" +Thread.currentThread().getName());
  27. } else {
  28. System.err.println("callback worker1 failure--" + SystemClock.now() + "----" + workResult.getResult()
  29. + "-threadName:" +Thread.currentThread().getName());
  30. }
  31. }
  32. }

通过这一个类看一下,action里就是你的耗时操作,begin就是任务开始执行时的回调,result就是worker执行完毕后的回调。当你组合了多个执行单元时,每一步的执行,都在掌控之内。失败了,还会有自定义的默认值。这是CompleteableFuture无法做到的。

安装教程

代码不多,直接拷贝包过去即可。

使用说明

一.3个任务并行

image.png

  1. ParWorker w = new ParWorker();
  2. ParWorker1 w1 = new ParWorker1();
  3. ParWorker2 w2 = new ParWorker2();
  4. WorkerWrapper<String, String> workerWrapper2 = new WorkerWrapper.Builder<String, String>()
  5. .worker(w2)
  6. .callback(w2)
  7. .param("2")
  8. .build();
  9. WorkerWrapper<String, String> workerWrapper1 = new WorkerWrapper.Builder<String, String>()
  10. .worker(w1)
  11. .callback(w1)
  12. .param("1")
  13. .build();
  14. WorkerWrapper<String, String> workerWrapper = new WorkerWrapper.Builder<String, String>()
  15. .worker(w)
  16. .callback(w)
  17. .param("0")
  18. .build();
  19. long now = SystemClock.now();
  20. System.out.println("begin-" + now);
  21. Async.beginWork(1500, workerWrapper, workerWrapper1, workerWrapper2);
  22. // Async.beginWork(800, workerWrapper, workerWrapper1, workerWrapper2);
  23. // Async.beginWork(1000, workerWrapper, workerWrapper1, workerWrapper2);
  24. System.out.println("end-" + SystemClock.now());
  25. System.err.println("cost-" + (SystemClock.now() - now));
  26. System.out.println(Async.getThreadCount());
  27. System.out.println(workerWrapper.getWorkResult());
  28. Async.shutDown();

二.1个执行完毕后,开启另外两个,另外两个执行完毕后,开始第4个

image.png

  1. ParWorker w = new ParWorker();
  2. ParWorker1 w1 = new ParWorker1();
  3. ParWorker2 w2 = new ParWorker2();
  4. ParWorker3 w3 = new ParWorker3();
  5. WorkerWrapper<String, String> workerWrapper3 = new WorkerWrapper.Builder<String, String>()
  6. .worker(w3)
  7. .callback(w3)
  8. .param("3")
  9. .build();
  10. WorkerWrapper<String, String> workerWrapper2 = new WorkerWrapper.Builder<String, String>()
  11. .worker(w2)
  12. .callback(w2)
  13. .param("2")
  14. .next(workerWrapper3)
  15. .build();
  16. WorkerWrapper<String, String> workerWrapper1 = new WorkerWrapper.Builder<String, String>()
  17. .worker(w1)
  18. .callback(w1)
  19. .param("1")
  20. .next(workerWrapper3)
  21. .build();
  22. WorkerWrapper<String, String> workerWrapper = new WorkerWrapper.Builder<String, String>()
  23. .worker(w)
  24. .callback(w)
  25. .param("0")
  26. .next(workerWrapper1, workerWrapper2)
  27. .build();
  28. long now = SystemClock.now();
  29. System.out.println("begin-" + now);
  30. Async.beginWork(3100, workerWrapper);
  31. // Async.beginWork(2100, workerWrapper);
  32. System.out.println("end-" + SystemClock.now());
  33. System.err.println("cost-" + (SystemClock.now() - now));
  34. System.out.println(Async.getThreadCount());
  35. Async.shutDown();

如果觉得这样不符合左右的顺序,也可以用这种方式:

  1. WorkerWrapper<String, String> workerWrapper = new WorkerWrapper.Builder<String, String>()
  2. .worker(w)
  3. .callback(w)
  4. .param("0")
  5. .build();
  6. WorkerWrapper<String, String> workerWrapper3 = new WorkerWrapper.Builder<String, String>()
  7. .worker(w3)
  8. .callback(w3)
  9. .param("3")
  10. .build();
  11. WorkerWrapper<String, String> workerWrapper2 = new WorkerWrapper.Builder<String, String>()
  12. .worker(w2)
  13. .callback(w2)
  14. .param("2")
  15. .depend(workerWrapper)
  16. .next(workerWrapper3)
  17. .build();
  18. WorkerWrapper<String, String> workerWrapper1 = new WorkerWrapper.Builder<String, String>()
  19. .worker(w1)
  20. .callback(w1)
  21. .param("1")
  22. .depend(workerWrapper)
  23. .next(workerWrapper3)
  24. .build();

三.复杂点的

image.png
在测试类里能找到,下图是执行结果。看时间戳,就知道执行的顺序。每个执行单元都是睡1秒。
image.png

  1. 依赖别的worker执行结果作为入参

可以从action的参数中根据wrapper的id获取任意一个执行单元的执行结果,但请注意执行顺序,如果尚未执行,则在调用WorkerResult.getResult()会得到null! image.pngimage.png

四.其他的详见test包下的测试类,支持各种形式的组合、编排。