Vertx HTTPClient 与 CompletableFuture 阻塞回调线程

我正面临一个非常奇怪的问题。


我正在使用 Vert.x 和处理程序,我正在使用 Vert.x 调用 REST API HttpClientRequest。现在我有一个CompletableFuture我正在HttpClientRequest. 后来,我使用CompletableFuture.get(). 但是无论何时get()调用方法,主线程都会被阻塞(正如预期的那样),但它会永远被阻塞。我没有看到回调发生在我的响应处理程序上并且它永远被卡住了。


这是代码:


import io.vertx.core.http.HttpClientRequest;

import io.vertx.core.http.HttpMethod;

import io.vertx.core.json.Json;

import io.vertx.core.json.JsonObject;

import java.util.concurrent.CompletableFuture;

import io.vertx.core.http.HttpClient;


CompletableFuture<JsonObject> comp = new CompletableFuture<JsonObject>();   

HttpClient httpClient = new HttpClient(); //This object initialized and set the endpoit, port and domain name.

HttpClientRequest request = httpClient.request(HttpMethod.POST, requestURI, response -> {

        response.bodyHandler(body -> {

            //do some process

            comp.complete(new JsonObject(body);

        });

    }).exceptionHandler(e -> {

        //log the error

        comp.completeExceptionally(e);

    });


request.end();

//after some process

comp.get();  // here main thread is stuck forever.

我的 API 给出了 200 个响应,我在 Wireshark 中看到了,如果我执行comp.thenAccept()回调,它会给出我的结果。


为什么会发生这种情况,解决方案是什么?


慕田峪7331174
浏览 587回答 2
2回答

湖上湖

您的实现的问题在于它违反了黄金法则 - 不要阻止事件循环。您不应该像CompletableFuture.get()在事件循环中那样调用阻塞操作。同样,sampleHandler()也不应该调用Thread.sleep()事件循环,但这是一个较小的问题。结果是你的事件循环现在被阻塞了……所以你的/sample请求不能再被处理了。并且由于请求没有被处理,你CompletableFuture仍然没有完成……死锁。这个问题有两种可能的解决方案:CompletableFuture按设计使用,依赖于链式调用而不是get(),尽管它不强制执行 Vert.x 的线程模型。例如:comp.whenComplete((result, e) -> {&nbsp; &nbsp; System.out.println("Got sample response");&nbsp; &nbsp; if (e != null) {&nbsp; &nbsp; &nbsp; &nbsp; context.response().setStatusCode(500)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .end(e.getMessage());&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; context.response().setStatusCode(200)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .putHeader("content-type", "text/html")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .end(result);&nbsp; &nbsp; }&nbsp; &nbsp; System.out.println("end testCompBlocking....");});使用 Vert.x 工具运行阻塞代码。这应该不是必需的,CompletableFuture但其他 API 可能需要它。例如:context.vertx().<String>executeBlocking(future -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String result = "Not Success";&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result = comp.get();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } catch (Exception e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println("Exception in getting from Completable..." + e.getMessage());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e.printStackTrace();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; future.complete(result);&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; false,&nbsp; &nbsp; &nbsp; &nbsp; result -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; context.response().setStatusCode(200);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; context.response().putHeader("content-type", "text/html");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; context.response().end(result.result());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println("end testCompBlocking....");&nbsp; &nbsp; &nbsp; &nbsp; });

拉丁的传说

get()&nbsp;阻塞主线程直到未来完成,但是,HttpClientRequest 在主线程上执行,因此这种情况会导致死锁。相反,它thenAccept()是非阻塞的,仅创建一个在未来完成时执行的回调。根据您提供的代码,您的用例不清楚;您是否有理由分别使用HttpClientandCompletableFuture而不是WebClientand&nbsp;Future?如果您需要使用 CompletableFuture,那么您应该查看此项目以获得与 Vert.x 更兼容的实现。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java