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