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());
                    }
                });
    	}

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM