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());
}
});
}
學習官方的代碼后,發現對業務異常也沒有好的實現。后面在個人的業務實現的時候可以考慮提供異步接口實現。畢竟異常一旦拋出,后面全部中斷,業務很難進行定位。