HTTP requests from a JS callout are not really behaving Asynchronously?

Not applicable

Team,

I was following this link and implemented Asynchrous HTTP Client Callout using JS approach.

https://community.apigee.com/content/kbentry/2340/asynchronous-http-requests-in-an-api-proxy.html

My Requirement:

I need to make 3 service callout asynchronously and get their response object - do mashup and give one single output. so i implemented HTTP service callout using JavaScript approach , but the results doesnt look its really asynchronous.

(I am making 3 JS Service callout under 1 JS file and storing the individual service response in session.context for mashup)

var calloutResponse1 = httpClient.get('http://httpbin.org/getService1');
calloutResponse1.waitForComplete();
context.session['calloutResponse1']= calloutResponse1;
var calloutResponse2 = httpClient.get('http://httpbin.org/getService2');
calloutResponse2.waitForComplete();
context.session['calloutResponse2]= calloutResponse2;
var calloutResponse3 = httpClient.get('http://httpbin.org/getService3');
calloutResponse3.waitForComplete();
context.session['calloutResponse3']= calloutResponse3;

Result::

Service1_Call Start Time :: Fri May 13 2016 11:15:40 GMT-0400 (EDT)

Service1_Call End Time :: Fri May 13 2016 11:15:46 GMT-0400 (EDT)

Total Time Taken by Service 1 =5674 msec

Service2_ Call Start Time :: Fri May 13 2016 11:15:46 GMT-0400 (EDT)

Service2_ Call End Time :: Fri May 13 2016 11:15:47 GMT-0400 (EDT)

Total Time Taken by Service 2 =1009 msec

Service3_ Call Start Time :: Fri May 13 2016 11:15:47 GMT-0400 (EDT)

Service3_Call End Time :: Fri May 13 2016 11:15:47 GMT-0400 (EDT)

Total Time Taken by Service 3 =34 msec

Though the Service1,2,3 Call Start Time is close - Parallel. the overall response time from this JS callout is

5674 +1009+34 =6717 mseconds instead of 5674 mseconds..

I was expecting this in Asynchronous flow

1. All 3 service callout should start at the same time..

2. The overall response time should be the response time of the max service response time & not sum of 3 service response time.. 5674 msec is what i am expecting here.

Please advice

Solved Solved
3 21 2,655
1 ACCEPTED SOLUTION

You need to move the waitForComplete calls to another policy. That's the call that forces the javascript execution to wait for a response. So you are absolutely right, you won't see asynchronous behavior this way. So yank those statements out of this policy, fire off the requests and store them in context, then in a second policy assemble the responses.

View solution in original post

21 REPLIES 21

You need to move the waitForComplete calls to another policy. That's the call that forces the javascript execution to wait for a response. So you are absolutely right, you won't see asynchronous behavior this way. So yank those statements out of this policy, fire off the requests and store them in context, then in a second policy assemble the responses.

Another option is to send out all the requests at one time, and only THEN wait for the responses. The way you have it in the current code, you send and wait for the first, send and wait for the second, send and wait for the third. You could parallelize them.

// send out three requests
var calloutResponse1 = httpClient.get('http://httpbin.org/getService1');
var calloutResponse2 = httpClient.get('http://httpbin.org/getService2');
var calloutResponse3 = httpClient.get('http://httpbin.org/getService3');
// now, wait for each one in turn
calloutResponse1.waitForComplete();
context.session['calloutResponse1']= calloutResponse1;
calloutResponse2.waitForComplete();
context.session['calloutResponse2]= calloutResponse2;
calloutResponse3.waitForComplete();
context.session['calloutResponse3']= calloutResponse3;

But as Carlos said, it might even be better to have some intervening policies between the sending and the waiting, if that works in your scenario.

@Dino - Even I tried your approach - i.e. in Policy 1 - I sent out all the requests at one time, and only THEN wait for the responses.. Here also - the policy 1 time of execution is sum of all 3 service response time & not the maximum service response time out of 3..

Hi Murali,

I think you may be interpreting your results incorrectly. when I tried this, I used 3 invocations to services that EACH take 5 seconds, the total time consumed is about 5050ms. I have verified that each service is responding independently.

My code looks like this:

API proxy:

    <Flow name='test 1'>
      <Description>insert description here</Description>
      <Request>
        <Step><Name>JS-SendThreeRequests</Name></Step>
      </Request>
      <Response>
        <Step><Name>JS-AwaitThreeResponses</Name></Step>
      </Response>
      <Condition>(proxy.pathsuffix MatchesPath "/t1") and (request.verb = "GET")</Condition>
    </Flow>

sendThreeRequests.js :

// send out three requests
for (var i=1; i<4; i++) {
  var response = httpClient.get('https://xxxx.apigee.net/api-test/delay');
  context.setVariable('calloutResponse' + i, response);
}

awaitThreeResponses.js:

// now, wait for each one in turn
var responses = [];
for (var i=1; i<4; i++) {
  var response = context.getVariable('calloutResponse' + i);
  response.waitForComplete();
  responses.push( response.isSuccess() ? response.getResponse().content : {});
}
context.setVariable('response.content', JSON.stringify({ responses: responses }, null, 2) + '\n');

In all cases, I see an individual call to /delay consume ~5000ms , and three calls structured like this consume about ~5050ms.

Can you do further testing to verify that what you think you see (no asynchronicity) is really occurring?

Also, to re-iterate: it's probably easier to get asynch behavior if you use nodejs.

@ Carlos Eberhardt

This is what I have done.

1. Policy 1- Make all my 3 service calls in a JS policy.. and put it in a session.context

var calloutResponse1 = httpClient.get('http://httpbin.org/ServiceCall1');

context.session['calloutResponse1']= calloutResponse1;

var calloutResponse2 = httpClient.get('http://httpbin.org/ServiceCall2');

context.session['calloutResponse2']= calloutResponse3;

var calloutResponse3 = httpClient.get('http://httpbin.org/ServiceCall3');

context.session['calloutResponse3']= calloutResponse3;

2. Policy 2 - Retrive the response object from session.context and invoke waitforComplete here

var exchangeData1 = context.session['calloutResponse1'];

exchangeData1.waitForComplete();

var responsePayload1 = exchangeData1.getResponse().content;

var exchangeData2 = context.session['calloutResponse2'];

exchangeData2.waitForComplete();

var responsePayload2 = exchangeData2.getResponse().content;

var exchangeData3 = context.session['calloutResponse3'];

exchangeData3.waitForComplete();

var responsePayload3 = exchangeData3.getResponse().content;

Expected Output:

1.Policy 1- I should see the same service invoking time for all 3 services.

Output -Yes it is calling all 3 services at the same time

2. Policy 2 : The maximum policy execution time should be the maximum Service Response Time

& not sum of all 3 services response time..

Output : it is still the sum of all 3 service response time. Please see below

Total Time taken by the exchangeData1: 5413

Total Time taken by the exchangeData2: 287

Total Time taken by the exchangeData3: 35

Total Time Taken for Policy 2 Execution : - 5735 msec (taken from my Trace Log)

Please advice what I am missing?

Note: you can format your code using the "code" button in the editor. It makes it easier for everyone to read.

How are the policies structured? Do you have one in the request and one in the response flow? Actually - scratch that question. I just tried with both policies in the response flow and I see a consistent behavior of all three requests taking just a little more time than the time required for the slowest request.

In short, I think you are misinterpreting your observations. I think the time taken from the trace log may be giving you an incorrect impression. Also, the fact that one of your requests takes much much longer than the others can lead you to incorrect conclusions. Is it possible for you to test this with 3 requests that all take over 1000 ms ? Ideally all within 2x of each other. Eg 1000, 2000, 3000 ms. The total time should be ~3030 ms, rather than 6000ms.

Please explore further.

@ Dino,This approach is working fine. Thanks for your help ..

@Diego Zuluaga I am going to try our node.js and compare the results. Will get back to you if i have any queries

Not applicable

@Murali, I understand that you're trying to implement this with JS policies and @Carlos Eberhardt and @Dino are spot on with recommendations. However, I think Node.js is really meant for this type of requirements. Here's a tutorial and a sample API Proxy demonstrating the same use case using async module. If you're more used to OOP, you can even transform async calls into Promises.

Ahhhh, very good point, Diego! Better to do this kind of thing in nodejs.

Hi Dino,

Thanks for your response.. I did nt get a chance to reverify my flow .. I will go through it again and get back to you

@Diego Zuluaga yes I am going to try our node.js also

Regards

Murali

Hi Diego,

I hope you are doing good. Now I am using Node JS to make Asynchronous calls.

I am facing one problem with the approach/code you shared with me last time(Please find the link below)

https://github.com/dzuluaga/apigee-tutorials/tree/master/apiproxies/apigee-nodejs-async

I am making mutiple service calls in async.parallel & this code is working fine if the payload size is less than 3 Mb from the target endpointsbut its failing for payload size greater than 3MB.

In the trace log, proxy says -response received from target, but its keep buffering for the data. Also when i looked at response.content - it was empty.Finally I am getting gateway timeout error.

Please advice how to resolve this problem using the code you shared with me earlier.

I thought I can use apigee.setVariable(res,'responseObject',results) and retrieve this variable on proxy response flow for response.content. but its not working..

function(error, results){

var end = new Date() - start;

console.log('execution time in seconds: ' + end/1000);

res.json("success");

apigee.setVariable(res, 'pdap.investment_service_response',results);

}

);

Regards

Murali

Hi Murali. Please ask new questions using the "Ask a Question" button.

3418-click-here.png

Hi @Murali, it' possible that streaming is required for your use case. Please try the settings described in this thread.

Best,

Diego

Hi Diego,

Thanks for your response. I implemented that fix in my target proxy,which is calling node js script. but i am getting error in the output due to Empty JSON string .Please advice

	 <ScriptTarget>
        <Properties>
            <Property name="keepalive.timeout.millis">60000</Property>
            <Property name="response.streaming.enabled">true</Property>
            <Property name="request.streaming.enabled">true</Property>
        </Properties>
        <ResourceURL>node://PDAP.RetrieveProductDataFromDataSources.js</ResourceURL>
    </ScriptTarget>
</TargetEndpoint>

Hi. can you ask a NEW question? This question is from May, surely it's not the same issue.

3619-ask-a-question-2.png

When you ask. the new question , be sure to provide the details around the nodejs script that you have a question about. Like, specifically WHAT in the script is not working.

We love to help, but asking and answering new questions in the comments, means no one else will be able to find the questions and answers.

adas
Participant V

This topic is already explained in this post: https://community.apigee.com/articles/2340/asynchronous-http-requests-in-an-api-proxy.html

Please refer to the same. It demonstrates how multiple backend calls can be made asynchronously and then the response can be mashed up. I think this is what you need.

@arghya das - the question is now "how to do it?" But , "now that I am doing it, please explain why i do not see asynch behavior?"

Yes .das- if you refer to my day 1 comments- i put this link as reference and tried everything mentioned there. but it is not working asynchronously for me.

As I mentioned - the all service callout happening at the same time.. but the overall policy execution time is sum of all 3 service callout response time & not the max response time of a the service callout.

> but it is not working asynchronously for me.

I think it is actually behaving asynchronously. I think perhaps you are interpreting the results you see, incorrectly.

Not applicable

Hi Diego,

I hope you are doing good. Now I am using Node JS to make Asynchronous calls.

I am facing one problem with the approach/code you shared with me last time(Please find the link below)

https://github.com/dzuluaga/apigee-tutorials/tree/master/apiproxies/apigee-nodejs-async

I am making mutiple service calls in async.parallel & this code is working fine if the payload size is less than 3 Mb from the target endpointsbut its failing for payload size greater than 3MB.

In the trace log, proxy says -response received from target, but its keep buffering for the data. Also when i looked at response.content - it was empty.Finally I am getting gateway timeout error.

Please advice how to resolve this problem using the code you shared with me earlier.

Regards

Murali

Hi Murali. Please ask new questions using the "Ask a Question" button.

3418-click-here.png