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