Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to block thread to wait for response in vert.x?

I have a situation where I call an external API A and use its response to feed to request of API B and call it and afterwards return the response to caller of API A. Something like below

   method(){
    response = call API A
    }

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

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

What I am facing here is that API A method is returning response without waiting for getting response from B.

I checked about executeBlocking method of vert.x and also tried of using 'blocking queue' but unable to achieve what I am intended to do. Can someone please direct me to correct way of doing it.Thanks in advance.

EDIT: Just to explain the exact scenario

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

}

I am using vert.x framework with Java. Now what happens during execution is, flow comes to STEP 1, initiate API A call. Goes to STEP 2 (without waiting for 'respFromApiA') and makes call to API B (which fails eventually because respFromApiA is NULL). And finally flow goes to STEP 3 and return from here. (without waiting for outcomes of API A and API B). If we see the print order it will be something like this

PRINT FINAL Response
PRINT API A response
PRINT API B response

What I am trying to achieve?

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

I hope I am able to make clear this time. Please let me know if you need further inputs.

like image 912
tausif Avatar asked May 13 '16 09:05

tausif


People also ask

What is non-blocking in Vertx?

One of the key advantages of Vert. x over many legacy application platforms is that it is almost entirely non-blocking (of kernel threads) - this allows it to handle a lot of concurrency (e.g. handle many connections, or messages) using a very small number of kernel threads, which allows it to scale very well.

How many Verticles do you need to deploy Vertx?

A Vert. x instance maintains N event loop threads (where N by default is core*2) by default. If you wanted to utilize all of your cores, you would deploy 2 verticles per core.

How do you turn off a Vertx?

You can programmatically tell Vert. x to stop: vertx. close();

What is event loop in Vertx?

The whole purpose of the event loop is to react to events which are delivered to the event loop by the operating system. Event loop processes those events by executing handlers. To explain how the event loop operates, let's imagine a typical HTTP server application serving multiple client connections at the same time.


2 Answers

Vert.x is highly asynchronous. Most operations will in fact return immediately but their results will be available to a Handler at a later point in time. So far so good. If I understand you correctly than you need to call B in the Handler of A. In this case A needs to be finished and the result would be available before you call B:

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

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

But what you are trying is to make something asynchronous synchron. You can not and should not try to make a asynchronous result available in the main program flow — that wouldn't work.

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 can't really return a result because it's calculated asynchronously. The result is only available for the Handler of Call_API_A (see my example above). Same for Call_API_B – so you can't return the result of Call_API_B. The caller of your class would also need to call your class with a Handler.

Now some additional information. In your case you have the problem that multiple asynchronous results depend on each other. Vert.x provides a much more convenient way to handle asynchronous results — the so called Futures. A Future (sometimes called Promise but in the Java world they are called Future) is a placeholder for results of asynchronous calls. Read about them in the documentation.

With a Future you can do something like this:

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

So instead of trying to return the asynchronous result of B in a synchronous way you should return a Future so the callee of your class could register for the asynchronous result of both A and B.

I hope this helps.

Edit: Future as return value

Lets say you want to wrap callA with so you can work with a Future. You could do it this way:

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

The Caller of this function can use the Future like this:

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

It also possible to compose multiple Future if you want to do them concurrently but they don't dependent on each other:

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

If you work in a asynchronous environment like Vert.x most of time you work with a soon-to-be-available-result aka Future. And in the Handler of a Future you often do another asynchronous call. You wrap a Future with a Future like the example of callB in callA's Handler.

How would you return the asynchronous result of a Future as an HTTP Response? Like this:

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);
    }
  });
});
like image 50
alexvetter Avatar answered Sep 30 '22 08:09

alexvetter


I have used Future to return some results to use it again in other methodes, this is my implementation i hope it helps someone :

 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;
}
like image 24
OLH Avatar answered Sep 30 '22 10:09

OLH