vertx中异步任务实现串行/并行执行以及回调

简述

串行执行异步任务

使用vertx框架编程的过程中,经常会遇到这种情况:需要处理一组异步任务,而且我们希望它们可以按照顺序执行下去,并且当它们全部执行完了得到通知,而且有时候也需要控制,当一个执行失败,后面的任务是否需要继续执行。这个就是按照顺序去执行异步任务,解决方案当然就是递归了,这个在我很早的博客当中已经写过了。多个异步请求保证执行顺序:用递归。但是呢,随着功能的一步步增多,这种需求是特别多的,你需要写大量的递归,我写着都烦了,那能不能封装一个执行器,替我们来递归执行任务呢,我们只需要按顺序提交并且提供一个回调处理器呢,答案是有的。

并行执行异步任务

另外一种场景:也是需要处理一组异步任务,但是它们是无序的,无序执行异步任务本身是很简单的,用普通的for循环就行了,但是最难的地方在于,它没有办法回调,就是不知道什么时候全部执行了。我们的需求就是需要执行一个回调器,全部执行完了以后通知我,并且告诉我,有哪些失败了,我再进一步处理。我把这个实现了以后,又去思考了上面的场景,也顺便实现了一下。

并行执行任务设计和实现

并行设计

想要实现这个,就是从下面几个方面去入手就行了:

  1. vertx提交的异步任务,真正的执行都会在另外一个线程里面(包括本身线程),至于说最终线程怎么执行的,我们其实不关心,关心的就是把这些任务一次性全部提交了,这个容易,for循环遍历一下就行了。
  2. 我们重点想要的是一个通知,全部执行完成的通知,那也就是应该当每一个任务执行完成以后,都应该告诉执行器,我执行完了,不管失败还是成功,然后执行器再去校验是否全部都通知过了,以此来判断是否发出最后的通知。那也就是说,对于提交的每一个任务,都有一个Future(在vertx里面代表一个异步操作的结果),用来表示它的状态,并且当完成以后,它的回调处理器就应该去校验所有的Future。
  3. 当所有都执行完毕了,我们就要去回调,回调什么呢?一个处理器,由用户提供,并且也是一个异步结果。
  4. 还有就是执行结果了,要么成功要么失败。暂时就定义成:全部成功算成功,一个失败就算失败。如果有多个失败的,但是我们只能抛一个异常,那就在一个异常里面把所有的错误情况都添加进去。

并行实现

根据上面的思考,就有了下面的实现,直接贴代码了。

/**
 * @Description 并行异步任务执行器
 * 用来解决场景:处理一组异步任务,任务之间没有顺序,但是希望在执行全部结束以后,进行回调。
 * 重点是提供回调的功能。因为普通的for循环就能够不按顺序执行它们,但是没有回调。
 * 
 **/
public class ParallelAsyncTaskExecutor implements AsyncResult<Void> {
    /**
     * 存储所有任务
     * key:任务。
     * value:是任务对应的异步结果。
     */
    private final Map<Handler<Future<Void>>, Future<Void>> tasks;
    private Handler<AsyncResult<Void>> handler;
    public ParallelAsyncTaskExecutor() {
        this.tasks = new HashMap<>();
    }

    /**
     * 添加一个异步任务,给这个任务挂上一个处理器,当任务执行以后就去校验是否所有的都执行完了。
     * @param handler
     */
    public void addAsyncTask(Handler<Future<Void>> handler){
        Future<Void> future = Future.future();
        future.setHandler(futureHandler -> {
            checkCallHandler();
        });
        this.tasks.put(handler, future);
    }

    /**
     * 检查并调用处理器
     */
    private void checkCallHandler() {
        if (isComplete()) {
            callHandler();
        }
    }

    private void callHandler(){
        if (handler != null) {
            handler.handle(this);
            handler = null;
        }
    }

    /**
     * 是否所有任务完成了
     * 挨个判断每个任务的结果。
     * @return
     */
    public boolean isComplete() {
        if (tasks.isEmpty()) {
            return false;
        }
        return tasks.values().stream().allMatch(future -> {
            return future.isComplete();
        });
    }

    /**
     * 执行启动。
     * for循环执行
     */
    public void start() {
        if (this.tasks.isEmpty()) {
            callHandler();
            return;
        }
        this.tasks.forEach((handler, future) -> {
            try {
                handler.handle(future);
            } catch (Throwable e) {
                future.tryFail(e);
            }
        });
    }

    public void setHandler(Handler<AsyncResult<Void>> handler) {
        this.handler = handler;
        checkCallHandler();
    }

    @Override
    public Void result() {
        return null;
    }

    /**
     * 失败原因,失败的情况下取出所有的失败的原因
     * @return
     */
    @Override
    public Throwable cause() {
        if (failed()){
            Exception e = new Exception("At least one task failed");
            tasks.forEach((consumer, future) -> {
                if (future.failed()){
                    if (e.getCause() == null) {
                        e.initCause(future.cause());
                    } else {
                        e.addSuppressed(future.cause());
                    }
                }
            });
            return e;
        }else{
            return null;
        }
    }

    /**
     * 所有成功就是成功了
     * @return
     */
    @Override
    public boolean succeeded() {
        return tasks.values().stream().allMatch(future -> future.succeeded());
    }

    /**
     * 任何一个失败就是失败了。
     * @return
     */
    @Override
    public boolean failed() {
        return tasks.values().stream().anyMatch(future -> future.failed());
    }

}

代码就不详细解释了,上面有一部分的注释。

并行用法

看一下测试代码,还是以部署Verticle为例:

        Vertx vertx=Vertx.vertx();
        List<String> verticles=new ArrayList<>();
        verticles.add("test");
        verticles.add("test1");
        ParallelAsyncTaskExecutor executor=new ParallelAsyncTaskExecutor();
        verticles.forEach(verticle->{
            executor.addAsyncTask(res->{
                vertx.deployVerticle(verticle,deployRes->{
                    if (deployRes.succeeded()){
                        System.out.println("部署成功:"+verticle);
                        res.complete();
                    }else{
                        System.out.println("部署失败:"+verticle);
                        res.fail(deployRes.cause());
                    }
                });
            });
        });
        executor.setHandler(res->{
            System.out.println("部署结束");
            if (res.succeeded()){
                System.out.println("全部部署成功");
            }else{
                System.out.println("部署失败:"+res.cause().getMessage());
                
            }
        });
        
        executor.start();

这边实现的效果就是一起并行部署多个verticle。这个里面有两个回调处理器,第一个是每个任务结束的回调,需要给执行器反馈一下执行结果,最后一个是所有执行完毕的一个回调。

串行执行任务设计和实现

串行设计

想要实现这个,就是从下面几个方面去入手就行了:

  1. 肯定要用递归。
  2. 因为是顺序执行,还需要维护一个当前执行的位置,下次执行的时候取出任务就行了。
  3. 还得要一个配置,用来标记是否需要:失败一个就停下来。
  4. 回调处理器和异常处理和上面的都是一样的。

实现了并行的,我才想到了实现这个,而且思路也比较清晰了,也就比较简单了。

串行实现

直接贴代码了

/**
 * @Description 串行异步任务执行器
 * 把一组有顺序的异步任务按照顺序执行。
 * 
 **/
public class SequentialAsyncTaskExecutor implements AsyncResult<Void>{
    private final List<Handler<Future<Void>>> tasks=new ArrayList<>();
    private final List<Future<Void>> futures=new ArrayList<>();
    private AtomicInteger pos=new AtomicInteger();
    /**
     * 是否执行中,执行中,暂不允许添加新的任务
     */
    private volatile boolean executing=false;
    /**
     * 失败一个就立刻停下来。默认为true。
     */
    private boolean errorStop=true;

    private Handler<AsyncResult<Void>> handler;

    public SequentialAsyncTaskExecutor() {
    }

    public void addAsyncTask(Handler<Future<Void>> handler){
        if (executing){
            throw new IllegalStateException("执行器已经启动了,不允许再添加新的任务");
        }
        Future<Void> future = Future.future();
        future.setHandler(res -> {
            pos.incrementAndGet();
            if (res.succeeded()){
                if (pos.get()<tasks.size()){
                    //还没有执行完。继续执行剩下的。
                    execute();
                }else{
                    //执行结束。
                    callHandler();
                }
            }else{
                //失败的情况下
                if (errorStop){
                    //失败一次直接结束的情况
                    callHandler();
                }else{
                    //失败了继续,这边也要分情况处理一下,看看还没有剩下的任务。
                    if (pos.get()<tasks.size()){
                        //还没有执行完。继续执行剩下的。
                        execute();
                    }else{
                        //执行结束。
                        callHandler();
                    }
                }
            }
        });
        this.tasks.add(handler);
        this.futures.add(future);
    }

    public void start(){
        if (this.handler==null){
            //不允许在执行过程中添加回调处理器。
            throw new IllegalArgumentException("回调处理器不能为空,请执行setHandler(Handler<AsyncResult<Void>> handler)方法");
        }
        execute();
    }


    private void callHandler(){
        handler.handle(this);
        handler = null;
    }

    private void execute(){
        Handler<Future<Void>> handler=tasks.get(pos.get());
        Future<Void> future=futures.get(pos.get());
        try {
            handler.handle(future);
        }catch (Throwable e){
            future.tryFail(e);
        }
    }

    public void setHandler(Handler<AsyncResult<Void>> handler) {
        this.handler = handler;
    }

    /**
     * 打开忽略错误的模式,一个一个全部执行,不管中间有没有错误,
     */
    public void openIgnoreErrorMode() {
        this.errorStop = false;
    }

    @Override
    public Void result() {
        return null;
    }

    @Override
    public Throwable cause() {
        if (failed()){
            Exception e = new Exception("At least one task failed");
            futures.forEach(future ->  {
                if (future.failed()){
                    if (e.getCause() == null) {
                        e.initCause(future.cause());
                    } else {
                        e.addSuppressed(future.cause());
                    }
                }
            });
            return e;
        }else{
            return null;
        }
    }

    @Override
    public boolean succeeded() {
        return futures.stream().allMatch(future -> future.succeeded());
    }

    @Override
    public boolean failed() {
        return futures.stream().anyMatch(future -> future.failed());
    }
}

不详细解释了,这边的注释也比较多。

串行测试

还是以部署Verticle为例,这次实现的效果就是按照顺序去部署。

public static void main(String[] args) {
        Vertx vertx=Vertx.vertx();
        List<String> verticles=new ArrayList<>();
        verticles.add("test");
        verticles.add("test1");
        SequentialAsyncTaskExecutor executor=new SequentialAsyncTaskExecutor();
        verticles.forEach(verticle->{
            executor.addAsyncTask(res->{
                vertx.deployVerticle(verticle,deployRes->{
                    if (deployRes.succeeded()){
                        System.out.println("部署成功:"+verticle);
                        res.complete();
                    }else{
                        System.out.println("部署失败:"+verticle);
                        res.fail(deployRes.cause());
                    }
                });
            });
        });
        executor.setHandler(res->{
            System.out.println("部署结束");
            if (res.succeeded()){
                System.out.println("全部部署成功");
            }else{
                System.out.println("部署失败:"+res.cause().getMessage());

            }
        });
        //打开忽略异常模式,失败了也继续执行
        //executor.openIgnoreErrorMode();
        executor.start();
    }

从代码上来看,和上面的写法是完全一样的。

总结

在代码的世界里,封装一些通用的工具类,能够提高不少效率。
在vertx的世界里,有了上面串行和并行执行异步任务的执行器,美滋滋。

猜你喜欢

转载自blog.csdn.net/ywg_1994/article/details/104420581