Vertx Future 异常处理

依然范特西╮ 提交于 2019-11-30 16:50:57

Vertx Future 异常处理

异常发生

​ 在使用Vertx进行开发的时候,必不可免使用Future异步编程框架。通过Future的 compose ,可以轻松实现不同异步任务的组合。

​ 但是在每个异步任务的处理过程中,异常的处理是开发者不得不考虑和头疼的问题。无法预知的中断会导致某次任务突然停止在某个阶段。下面是Future代码Demo还原:

    private Promise<String> getResponse()  {
        Promise<String> promise = Promise.promise();
        int[] i = new int[1];
        LOGGER.info(" throw exception {}", i[2]);
        promise.complete("success");
        return promise;
    }

接口Demo如下

            // This handler gets called for each request that arrives on the server
            HttpServerResponse response = request.response();
            Promise<String> promise = getResponse();
            response.putHeader("content-type", "text/plain");
            promise.future().setHandler(result -> {
                LOGGER.info("result status is {}", result.succeeded());
            });
            // Write to the response and end it
            response.end("Hello World!");

该异常 ArrayIndexOutOfBoundsException 会阻断程序的进行,服务永远不会返回Hello World给客户端。

异常导致后续业务中断

上述demo的异步接口在执行的时候,为什么会导致后面的response.end.... 无法执行。是因为这个异步接口执行过快,在这个线程内执行到了异常,来不及执行response.end方法,就已经失败了。下面Demo演示

    private Promise<String> getResponse() {

        Promise<String> promise = Promise.promise();
        int[] i = new int[1];
        // 为了避免接口正常返回业务逻辑执行,设置异常延迟执行
        vertx.setTimer(5000, ar -> {
            // 此时 异步接口开始输出返回结果
            promise.fail("error");
            LOGGER.info(" throw exception {}", i[2]);
        });
        return promise;
    }

​ 通过实测当使用无返回值的代码时,接口顺利返回了,同时异步接口一直在等待执行结果输出,说明后续业务逻辑正常执行,异步接口一直在等待。这个是符合逻辑。

    private Promise<String> getResponse()  {

        Promise<String> promise = Promise.promise();
        int[] i = new int[1];
//        LOGGER.info(" throw exception {}", i[2]);
        // 始终不返回执行结果
//        promise.complete();
        return promise;
    }

异常导致compose关联业务无法继续执行

​ 在业务依赖的情况下,比如上面的结果需要被后面的业务使用,这时候就需要去处理异常了。但是感觉每个正常业务逻辑上都写这种Cache异常处理很挫。

​ 这时候发现Vertx提供的阻塞执行代码 executeBlocking 中抛出了异常,但是异步并未中断,而且成功返回了异常了。

            vertx.executeBlocking(ar -> {
                int[] i = new int[1];
                LOGGER.info(" throw exception {}", i[2]);
                ar.complete("finish");
            }, ar -> {
                LOGGER.info("result status is {}, result is {}", ar.succeeded(), ar.result());
            });

输出日志如下:

[20:51:36:262] [vert.x-eventloop-thread-0][INFO] - com.vertx.verticle.WebVerticle.lambda$start$2(WebVerticle.java:36) - response to client request
[20:51:36:267] [vert.x-eventloop-thread-0][INFO] - com.vertx.verticle.WebVerticle.lambda$null$1(WebVerticle.java:34) - result status is false, result is null

可以看到,异步已经执行结束,并且结果为false。分析追踪Vertx的executeBlocking 的源代码可以发现他调用了 ContextImpl类的executeBlocking方法。摘选关键代码并添加注释如下:

// blockingCodeHandler 对应上面的业务代码
// resultHandler 对应业务代码执行的结果处理
<T> void executeBlocking(Handler<Promise<T>> blockingCodeHandler,
                             Handler<AsyncResult<T>> resultHandler) {
                Promise<T> promise = Promise.promise();
                // 此处代码捕获了所有可抛的异常,一旦有异常产生,就由框架对业务代码返回配置
                try {
                    blockingCodeHandler.handle(promise);
                } catch (Throwable e) {
                    promise.tryFail(e);
                } 
                Future<T> res = promise.future();
                res.setHandler(ar -> {
                    if (resultHandler != null) {
                        runOnContext(v -> resultHandler.handle(ar));
                    } else if (ar.failed()) {
                        // 如果用户未指定异常处理并且执行失败,打印异常
                        reportException(ar.cause());
                    }
                });
        }

学习官方的代码后,发现对业务异常也没有好的实现。后面在个人的业务实现的时候可以考虑提供异步接口实现。毕竟异常一旦抛出,后面全部中断,业务很难进行定位。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!