2016-05-13 28 views
5

Mam sytuacji gdzie mam zadzwonić do zewnętrznego interfejsu API A i wykorzystanie jej odpowiedź karmić się wniosek API B i nazwać, a następnie powrócić do odpowiedzi wywołującego API A. coś takiego jak poniżejJak zablokować wątek, aby czekać na odpowiedź w pliku vert.x?

method(){ 
    response = call API A 
    } 

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

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

Co ja Tutaj stoi, że metoda API A zwraca odpowiedź, nie czekając na otrzymanie odpowiedzi z B.

Sprawdziłem metodę executeBlocking w vert.x, a także próbowałem użyć "kolejki blokującej", ale nie mogłem osiągnąć tego, do czego jestem przeznaczony zrobić. Czy ktoś może skierować mnie do prawidłowego sposobu robienia tego. Dzięki za to.

EDIT: Just do wyjaśnienia dokładnego scenariusza

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

} 

Używam vert.x ramy z Java. Teraz, co się dzieje podczas wykonywania, przepływ przychodzi do KROKU 1, zainicjować wywołanie API A. Przechodzi do KROKU 2 (bez czekania na "respFromApiA") i wykonuje wywołanie API B (które nie powiedzie się ostatecznie, ponieważ respFromApiA ma wartość NULL). I na koniec przepływ przechodzi do KROKU 3 i powrót stąd. (bez czekania na wyniki API A i API B). Jeśli widzimy kolejność wydruku będzie to coś takiego

PRINT FINAL Response 
PRINT API A response 
PRINT API B response 

Co staram się osiągnąć?

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

Mam nadzieję, że tym razem będę w stanie to wyjaśnić. Proszę dać mi znać, jeśli potrzebujesz dalszych danych wejściowych.

+0

Dlaczego nie można zrobić oba połączenia w "metoda" sekwencyjnie? – Fildor

+0

Zgaduję, że wykonujesz połączenia bez blokowania. Najprawdopodobniej zwracają swoje odpowiedzi w wywołaniu zwrotnym (nie wiem vert.x, przepraszam). Musisz więc znaleźć albo możliwość poinformowania vert.x, że chcesz zablokować te połączenia i zwrócić wynik, albo musisz użyć wywołań zwrotnych. – Fildor

+0

Może gdybyś pokazał więcej kodu (prawdziwy kod), wtedy ktoś z doświadczeniem w vert.x mógłby pomóc ... – Fildor

Odpowiedz

0

Masz trzy opcje:

  1. wykonaj API Wywołanie aw zwrotnego zrobić wywołanie API B.
  2. Użyj szkieletu asynchronicznego (https://spring.io/guides/gs/async-method/) do wyboru, który będzie uruchamiał oba wywołania interfejsu API równolegle.
  3. To samo co pierwsze, ale z obietnicami.

drugie jest najlepszym rozwiązaniem, ponieważ będzie to znacznie szybciej i można dodać API C przy niewielkim wysiłku

+0

O ile zrozumiałem pytanie, jest pewien wynik z Wezwania do B, który jest potrzebny jako Param do Wezwania do A. Tak więc połączenie równoległe nie ma większego sensu. – Fildor

+0

Zrozumiałem, że wyjście A i B ma coś zrobić. Może mój zły. –

+0

Odniosłem się do tej części pytania: "Wzywam zewnętrzny interfejs API A i używam jego odpowiedzi do kanału na żądanie API B". Może @tausif może to trochę wyjaśnić. – Fildor

11

Vert.x jest wysoce asynchroniczny. Większość operacji natychmiast powróci, ale ich wyniki będą dostępne w późniejszym czasie. Jak na razie dobrze. Jeśli rozumiem cię poprawnie, niż musisz zadzwonić pod numer B w Handler z A. W tym przypadku A musi być zakończona, a wynik byłby dostępny przed wywołaniem B:

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

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

Ale to, co staramy się zrobić coś jest asynchroniczny synchron. Nie możesz i nie powinieneś próbować udostępniać asynchronicznego wyniku w głównym strumieniu programu - to by nie działało.

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 naprawdę nie można zwrócić wynik, ponieważ jest obliczane asynchronicznie. Wynik jest dostępny tylko dla Handler z Call_API_A (zobacz mój przykład powyżej). To samo dla Call_API_B - więc nie możesz zwrócić wyniku Call_API_B.Osoba dzwoniąca z twojej klasy również będzie musiała zadzwonić do twojej klasy z numerem Handler.

Teraz dodatkowe informacje. W twoim przypadku masz problem, że wiele asynchronicznych wyników zależy od siebie. Vert.x zapewnia znacznie wygodniejszy sposób obsługi wyników asynchronicznych - tzw. Futures. A Future (czasami nazywane Promise, ale w świecie Java nazywają się Future) jest symbolem zastępczym wyników wywołań asynchronicznych. Przeczytaj o nich w documentation.

Z Future można zrobić coś takiego:

Future<...> callAFuture = Future.future(); 
callA(asyncResultA -> { 
    if (asyncResultA.succeeded()) { 
    System.out.println("A finished!"); 
    callAFuture.complete(asyncResultA.result()); 
    } else { 
    callAFuture.fail(asyncResultA.cause()); 
    } 
}); 

Więc zamiast próbować zwrócić wynik asynchroniczny z B w synchroniczny sposób należy zwrócić Future więc wywoływany swojej klasie mogłaby zarejestrować asynchroniczny wynik obu: A i B.

Mam nadzieję, że to pomoże.

Edit: Future jako wartości zwracanej

Powiedzmy chcesz zawinąć callA z tak można pracować z Future. Można zrobić to w ten sposób:

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

wywołującego tej funkcji mogą korzystać przyszłość tak:

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

Jest również możliwe, aby skomponować wielokrotność Future jeśli chcesz zrobić je równolegle, lecz oni nie „T od siebie zależne:

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

Jeśli pracujesz w środowisku asynchronicznym jak Vert.x większość czasu pracy z wkrótce-do-dostępne- wynik aka Future. I w Handler z Future często wykonuje się kolejne wywołanie asynchroniczne. Zawijacie Future z Future, tak jak na przykład callB w callA 's Handler.

Jak można zwrócić asynchroniczny wynik Future jako odpowiedź HTTP? Tak:

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); 
    } 
    }); 
}); 
+0

Dzięki @Alexvetter za odpowiedź. Zgadzam się z logiką wywołania API_B w module obsługi API_A. To próbowałem już i działa. Miarake robię to, próbując zwracać odpowiedź do abonenta klasy synchronicznie, który powinien być obsługiwany przez program obsługi. Aby usunąć jeden punkt, na koniec muszę zwrócić odpowiedź API_B do osoby dzwoniącej. Powiedziałeś o użyciu przyszłego obiektu. Więc po prostu wprowadzamy typ zwrotu jako Przyszłość i zwracamy przyszły obiekt do osoby dzwoniącej, aby uzyskać odpowiedź? – tausif

+0

Dokładnie powinieneś zagrać trochę z 'Future' i jego metodą' setHandler'. – alexvetter

+0

Ponieważ Vert.x jest wysoce asynchroniczny, Twoja aplikacja przekaże 'Futures' i skomponuje je zgodnie z potrzebami. To, że nie blokujesz pętli zdarzeń! – alexvetter

1

Użyłem Future wrócić trochę results użyć go ponownie w innych méthodes, to moja implementacja mam nadzieję, że ktoś pomaga:

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