2016-05-13 1 views
5

У меня есть ситуации, когда я называю внешний API A и использовать свой ответ на корм просьбу API B и назвать его, а затем возвращает ответ вызывающего API А. Что-то вроде нижеКак заблокировать поток, чтобы ждать ответа в 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 
    } 

Я Я сталкиваюсь здесь, что метод API A возвращает ответ, не дожидаясь получения ответа от B.

Я проверил способ executeBlocking для vert.x, а также попытался использовать «блокирующую очередь», но не смог достичь того, что я намерен делать. Может кто-то, пожалуйста, направить меня, чтобы исправить способ сделать это. Спасибо заранее.

EDIT: Просто чтобы объяснить точный сценарий

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

} 

Я использую vert.x рамки с Java. Теперь, что происходит во время выполнения, поток переходит к шагу 1, инициирует вызов API. Переход к STEP 2 (не дожидаясь 'respFromApiA') и вызывает вызов API B (который не сработает, потому что respFromApiA - NULL). И, наконец, поток переходит к STEP 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. 

Надеюсь, я смогу сделать ясно, на этот раз. Пожалуйста, дайте мне знать, если вам нужны дополнительные материалы.

+0

Почему вы не делаете обоих вызовов в 'методе' последовательно? – Fildor

+0

Я думаю, вы делаете неблокирующие вызовы. Скорее всего, они возвращают свои ответы в обратном вызове (я не знаю vert.x, извините). Поэтому вам нужно найти либо возможность указать vert.x, что вы хотите, чтобы эти вызовы блокировались и возвращали результат, или вам нужно использовать обратные вызовы. – Fildor

+0

Возможно, если вы указали больше кода (реального кода), то кто-то с опытом работы с vert.x мог бы помочь ... – Fildor

ответ

0

У вас есть три варианта:

  1. Продлайт API Вызов и обратный вызов делает B вызова API.
  2. Используйте async framework (https://spring.io/guides/gs/async-method/) по вашему выбору, который будет запускать оба вызова api параллельно.
  3. Как и раньше, но с обещаниями.

Второй является лучшим решением, так как он будет значительно быстрее будет, и вы можете добавить API C с небольшим усилием

+0

Насколько я понял вопрос, есть некоторый результат от Call to B, который необходим как Param для вызова А. Так что параллельный вызов на самом деле не имеет смысла. – Fildor

+0

Я понял, что выход A и B должен что-то делать. Может быть, я плохой. –

+0

Я имел в виду эту часть вопроса: «Я вызываю внешний API A и использую его ответ для подачи запроса API B». Возможно, @tausif может сделать это немного понятнее. – Fildor

11

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 не может действительно возвращать результат, поскольку он рассчитывается асинхронно. Результат доступен только для HandlerCall_API_A (см. Мой пример выше). То же самое для Call_API_B - так что вы не можете вернуть результат Call_API_B.Вызывающий ваш класс также должен будет вызвать ваш класс с помощью Handler.

Теперь дополнительная информация. В вашем случае у вас есть проблема, что несколько асинхронных результатов зависят друг от друга. Vert.x обеспечивает гораздо более удобный способ обработки асинхронных результатов - так называемый Futures. A Future (иногда называемый Promise, но в мире Java они называются Future) является заполнитель для результатов асинхронных вызовов. Читайте о них в documentation.

С 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.

Надеюсь, это поможет.

Edit: Будущее, как возвращаемое значение

Допустим, вы хотите, чтобы обернуть 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 большую часть времени вы работаете с скоро-к-быть-имеющимся в наличии aka Future. И в HandlerFuture вы часто выполняете другой асинхронный вызов. Вы заверните Future с Future, как пример callB в 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); 
    } 
    }); 
}); 
+0

Спасибо @Alexvetter за ответ. Я согласен с логикой вызова API_B в обработчике API_A. Это я уже пробовал, и он работает. Ошибка, которую я делаю, заключается в попытке вернуть ответ вызывающему классу синхронно, который должен обрабатываться через обработчик. Чтобы очистить одну точку, в конце мне нужно вернуть ответ API_B вызывающему. Вы сказали об использовании будущего объекта. Итак, мы просто возвращаем тип как Будущее и возвращаем будущий объект вызывающему, чтобы получить ответ? – tausif

+0

Точно, вы должны немного поиграть с 'Future' и его методом' setHandler'. – alexvetter

+0

Поскольку Vert.x очень асинхронен, ваше приложение будет использовать 'Futures' и компоновать его по мере необходимости. Это то, что вы не блокируете Event Loop! – alexvetter

1

Я использовал Future вернуть некоторые results использовать его снова в других methodes, это моя реализация я надеюсь, что это помогает кто-то:

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