如何阻止线程在vert.x中等待响应?

时间:2016-05-13 09:30:55

标签: java reactive-programming vert.x event-driven

我有一种情况,我调用外部API A并使用其响应来提供API B的请求并调用它,然后将响应返回给API A的调用者。如下所示

   method(){
    response = call API A
    }

    method_for_API_A(){
      handler() ->{
      API_B
      }
    return response;
    }

    method_for_API_B(){
    //code to call API B
    }

我在这里面临的是API A方法正在返回响应而不等待从B获得响应。

我检查了vert.x的executeBlocking方法,并尝试使用'阻塞队列',但无法实现我打算做的事情。 有人可以指导我做正确的方法。谢谢。

编辑:只是为了解释具体情况

Class MyClass{
 public Response method_A (Request request){
 String respFromApiA = Call_API_A(request) ;  // STEP 1
 Response respFromApiB = Call_API_B(request, respFromApiA); // STEP 2
 Print(respFromApiB)   // PRINT FINAL Response
 return respFromApiB; // STEP 3
}

String Call_API_A(Request request){
// Implementation
Print(string);  // PRINT API A response
return string
}

Response Call_API_B(Response response){
// Implementation
Print(response);  // PRINT API B response
return response;
}

}

我正在使用带有Java的vert.x框架。 现在执行过程中发生的是,流程进入步骤1,启动API A调用。转到步骤2(不等待'respFromApiA')并调用API B(最终因为respFromApiA为NULL而失败)。最后流程进入第3步并从此处返回。 (无需等待API A和API B的结果)。 如果我们看到打印订单,它将是这样的

PRINT FINAL Response
PRINT API A response
PRINT API B response

我想要实现的目标是什么?

Wait for API A response.
Make call to API B. Wait for API B response.
Return response got from API B.

我希望这次我能说清楚。如果您需要进一步的输入,请告诉我。

3 个答案:

答案 0 :(得分:13)

Vert.x是高度异步的。大多数操作实际上会立即返回,但其结果将在稍后的某个时间段提供给Handler。到现在为止还挺好。如果我理解正确,则需要在B Handler中致电A。在这种情况下,A需要完成,并且在您致电B之前结果可用:

callA(asyncResultA -> {
  System.out.println("Result A: " + asyncResultA.result());

  callB(asyncResultB -> {
    System.out.println("Result B:" + asyncResultB.result());
  });
});

但你正在尝试的是使异步同步。您不能也不应该尝试在主程序流中提供异步结果 - 这是行不通的。

String respFromApiA = Call_API_A(request); // STEP 1
Response respFromApiB = Call_API_B(request, respFromApiA); // STEP 2
Print(respFromApiB); // PRINT FINAL Response
return respFromApiB; // STEP 3

Call_API_A无法真正返回结果,因为它是异步计算的。结果仅适用于Handler的{​​{1}}(请参阅上面的示例)。 Call_API_A也是如此 - 因此您无法返回Call_API_B的结果。您班级的来电者还需要使用Call_API_B来调用您的班级。

现在有一些额外的信息。在您的情况下,您遇到的问题是多个异步结果相互依赖。 Vert.x提供了一种更方便的方法来处理异步结果 - 即所谓的HandlerFutures(有时称为Future但在Java世界中称为Promise)是异步调用结果的占位符。在documentation中了解它们。

使用Future,您可以执行以下操作:

Future

因此,不应尝试以同步方式返回Future<...> callAFuture = Future.future(); callA(asyncResultA -> { if (asyncResultA.succeeded()) { System.out.println("A finished!"); callAFuture.complete(asyncResultA.result()); } else { callAFuture.fail(asyncResultA.cause()); } }); 的异步结果,而应返回B,以便类的被调用者可以注册Future的异步结果和A

我希望这会有所帮助。

编辑:未来作为返回值

假设您要包装B,以便可以使用callA。你可以这样做:

Future

此功能的来电者可以像这样使用未来:

public Future<String> doSomethingAsync() {
  Future<String> callAFuture = Future.future();

  // do the async stuff
  callA(asyncResultA -> {
    if (asyncResultA.succeeded()) {
      System.out.println("A finished!");
      callAFuture.complete(asyncResultA.result());
    } else {
      callAFuture.fail(asyncResultA.cause());
    }
  });

  // return Future with the asyncResult of callA
  return callAFuture;
}

如果您想同时执行多个Future<String> doSomethingFuture = doSomethingAsync(); doSomethingFuture.setHandler(somethingResult -> { // ... doSomethingAsync finished }); ,也可以组合多个Future,但它们并不相互依赖:

CompositeFuture.all(futureA, futureB).setHandler(connections -> {
  // both Futures completed
});

如果你在像Vert.x这样的异步环境中工作,大部分时间都使用即将可用的结果又名Future。在Handler的{​​{1}}中,您经常会进行另一次异步调用。您将FutureFuture Future callB中的callA一样包裹Handler

如何将Future的异步结果作为HTTP响应返回?像这样:

router.route("/").handler(routingContext -> {
  HttpServerResponse response = routingContext.response();

  Future<String> future = doSomethingAsync();
  future.setHandler(somethingResult -> {
    if (somethingResult.succeeded()) {
      response
        .end(somethingResult.result());
    } else {
      routingContext.fail(500);
    }
  });
});

答案 1 :(得分:1)

我已经使用Future返回一些results以便在其他方法中再次使用它,这是我的实现,我希望它可以帮助某人:

 public static void ussdMessages(RoutingContext routingContext){
    String codeService = routingContext.getBodyAsJson().getString("codeService");
    Future<String> futureQuery=getServiceQuery(codeService);
    Future<JsonObject> futureParams = getServiceParams(codeService);
    CompositeFuture.all(futureQuery,futureParams).setHandler(r->{
        System.out.println(futureQuery.result());
        System.out.println(futureParams.result());
    });

}

public static Future<JsonObject> getServiceParams(String codeService){
    Future<JsonObject> future=Future.future();
    JsonObject params = new JsonObject();
    params.put("QUERY", Queries.DB_SELECT_SERVICE_PARAMS);
    params.put("PARAMS", new JsonArray().add(codeService));
    DB.select(params, res -> {
        if (res.succeeded()) {
            future.complete(res.result());
        } else {
            future.fail(res.cause().getMessage());
        }
    });
    return future;
}


public  static Future<String>  getServiceQuery(String codeService){
    Future<String> future = Future.future();
    JsonObject params = new JsonObject();
    params.put("QUERY", Queries.DB_SELECT_SERVICE_QUERY);
    params.put("PARAMS", new JsonArray().add(codeService));
    System.out.println(params);
    DB.select(params, res -> {
        if (res.succeeded()) {
          // query = res.result().getJsonArray("results").getJsonArray(0).getString(0);
            future.complete(res.result().getJsonArray("results").getJsonArray(0).getString(0));
        } else {
            future.fail(res.cause().getMessage());
        }
    });
    return future;
}

答案 2 :(得分:0)

您有三种选择:

  1. 执行API A调用并在回调中执行API B调用。
  2. 使用您选择的异步框架(https://spring.io/guides/gs/async-method/)并行运行两个api调用。
  3. 与第一个相同,但有承诺。
  4. 其次是最佳解决方案,因为它会快得多,您可以轻松添加API C