web-dev-qa-db-ja.com

vert.xで応答を待つためにスレッドをブロックする方法は?

外部APIAを呼び出し、その応答を使用してAPI Bのリクエストにフィードし、それを呼び出した後、APIAの呼び出し元に応答を返す状況があります。以下のようなもの

   method(){
    response = call API A
    }

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

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

ここで私が直面しているのは、APIAメソッドが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に到達し、APIA呼び出しを開始することです。ステップ2に進み(「respFromApiA」を待たずに)、API Bを呼び出します(respFromApiAがNULLであるため、最終的に失敗します)。そして最後に、フローはステップ3に進み、ここから戻ります。 (APIAおよびAPIBの結果を待たずに)。印刷注文を見ると、次のようになります

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.

今回ははっきりさせたいと思います。さらに入力が必要な場合はお知らせください。

8
tausif

Vert.xは高度に非同期です。ほとんどの操作は実際にはすぐに返されますが、その結果は後の時点でHandlerが利用できるようになります。ここまでは順調ですね。私があなたを正しく理解しているなら、あなたがBHandlerAを呼び出す必要があるよりも。この場合、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は非同期で計算されるため、実際には結果を返すことができません。結果は、Call_API_AHandlerでのみ使用できます(上記の私の例を参照)。 Call_API_Bについても同じです。したがって、Call_API_Bの結果を返すことはできません。クラスの呼び出し元も、Handlerを使用してクラスを呼び出す必要があります。

今、いくつかの追加情報。あなたの場合、複数の非同期結果が互いに依存しているという問題があります。 Vert.xは、非同期結果を処理するためのはるかに便利な方法、いわゆるFuturesを提供します。 FuturePromiseと呼ばれることもありますが、Javaの世界では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を返して、クラスの呼び出し先がABの両方の非同期結果に登録できるようにする必要があります。

これがお役に立てば幸いです。

編集:戻り値としての未来

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を使用できます。

Future<String> doSomethingFuture = doSomethingAsync();
doSomethingFuture.setHandler(somethingResult -> {
  // ... doSomethingAsync finished
});

複数のFutureを同時に作成したいが、それらが相互に依存していない場合は、それらを作成することもできます。

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

Vert.xのような非同期環境で作業する場合、ほとんどの場合、soon-to-be-available-result別名Futureで作業します。また、HandlerFutureでは、別の非同期呼び出しを行うことがよくあります。 FutureFuturecallBの例のように、callAHandlerでラップします。

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);
    }
  });
});
15
alexvetter

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;
}
3
OLH

3つのオプションがあります。

  1. API A呼び出しを実行し、コールバックでAPIB呼び出しを実行します。
  2. 両方のAPI呼び出しを並行して実行する、選択した非同期フレームワーク( https://spring.io/guides/gs/async-method/ )を使用します。
  3. 最初と同じですが、約束があります。

2番目は、かなり高速で、わずかな労力でAPI Cを追加できるため、最良のソリューションです。

0
Martin Gottweis