espider
espider

Reputation: 133

What is the correct behavior for Node.js while Node.js is requesting a long time http request for other servers

what is the correct behavior for Node.js while Node.js is requesting a long time http request. The situation is that, my node.js server acts just like a web proxy between client side and db layer(which is exposed by web service). The client will send many requests to Node.js at the same time, the node.js will use request.js pass the requests to the web service. Some of the web services need seconds to get response. Because the handler of response is using callback way in request.js, it should be pushed into the event loop, and the node.js should move to hand over the new request and won't wait for the response. But in my application, the node.js won't process the new request until get the previous response. I am not sure which one is the correct behavior. For the request.js, I have added a agent in option to avoid blocked by default socket. var requestObj = { url : targetUrl, json : true, pool: new http.Agent() }

Please help me on this. If the node.js won't process request until get response, I can't see the reason to use node.js, so I think the issue is caused by my incorrect code. Below are the class to send http request.

var request = require('request');
var http = require('http');

function apiService(){}

apiService.prototype.get = function (context, payload, callback, url){
	return this.requestAPI(context, payload, callback, url, "GET");
}
apiService.prototype.post = function(context, payload, callback, url){
	return this.requestAPI(context, payload, callback, url, "POST");
}
apiService.prototype.del = function(context, payload, callback, url){
	return this.requestAPI(context, payload, callback, url, "DELETE");
}
apiService.prototype.requestAPI = function(context, payload, callback, url, method){

	var config = context.config;
	var targetUrl = config.APIHost
					+ (config.port == null ? "": (":" + config.port)) 
					+ "/" 
					+ config.baseDir
					+ "/"
					+ url;
	
	var requestObj = {
			url : targetUrl,
			json : true,
			pool: new http.Agent()
		}
	
	if (config.proxy != null){
		requestObj.proxy = config.proxy;
	}
	
	switch (method){
		case "POST":
			requestObj.body = payload;
			request.post(requestObj, function (err, resp, body){
				if (err){
					callback(err);
					return;
				}
				callback(null, body);
			});
			break;
		case "GET":
			var queryString = "";
			for (att in payload){
				if (queryString != "")
					queryString += "&";
				queryString += att.toString() + "=" + payload[att];
			}
			if (queryString!="")
				requestObj.url += "?" + queryString;
		
			request.get(requestObj, function (err, resp, body){
				if (err){
					callback(err);
					return;
				}
				callback(null, body);
			});
			break;
		case "DELETE":
			requestObj.body = payload;
			request.del(requestObj, function (err, resp, body){
				if (err){
					callback(err);
					return;
				}
				callback(null, body);
			});
			break;
	}
}

Updated: Current process

client       request1  ->       Node
client       request2  ->       Node
                                Node    server request1    ->backend
                                Node     <- server response1 backend
client       <- response1       Node
                                Node    server request2    ->backend
                                Node     <-  server response2 backend
client       <- response2       Node 

What I think it should be

  client       request1  ->       Node
client       request2  ->       Node
                                Node    server request1 ->     backend
                                Node    server request2 ->     backend
                                Node    <- server response2    backend
client       <-response2        Node
                                Node    <- server response1    backend 
client       <-response1        Node

Upvotes: 1

Views: 993

Answers (1)

jozzy
jozzy

Reputation: 2943

You are creating a new agent instance (a new pool) for every invocation of requestAPI method invocation . A default agentInstance with no maxsockets set will have 5 as its default value.

You could either increase the size of the global agent as in http.globalAgent.maxSockets = Infinity or a high value depending on your reqs and remove the custom pool you have created

or

define the custom pool at the global level and set the maxsockets

var myAPIPool = new http.Agent({"maxSockets" : Infinity}); // Infinity or a high value depending on your res.

Then use this variable inside your method

var requestObj = {
            url : targetUrl,
            json : true,
            pool: myAPIPool
        }

UPDATED

On playing around with request.js i came to following realization . If you are calling http methods on request module, the callbacks need to be provided using the on method. eg. )

  request.get(requestObj).on('response', function (response){
                callback(null, body);
            });

Similarly you would need to provide error callbacks chaining this pattern. ie.) .on("error',cb) etc

This will give you the expected behavior.

UPDATED:

I tested the following code, minor tweaks of your version without any proxies. This clearly logs the calls and when all the calls are finished log s the callbacks

var request = require('request');
var http = require('http');

function apiService() {}

apiService.prototype.get = function(context, payload, callback, url) {
    return this.requestAPI(context, payload, callback, url, "GET");
}
apiService.prototype.post = function(context, payload, callback, url) {
    return this.requestAPI(context, payload, callback, url, "POST");
}
apiService.prototype.del = function(context, payload, callback, url) {
    return this.requestAPI(context, payload, callback, url, "DELETE");
}
apiService.prototype.requestAPI = function(context, payload, callback, url, method) {

    var requestObj = {
        url: url,
        json: true,
        pool: new http.Agent()
    }

    switch (method) {
        case "GET":
            request.get(requestObj, function(err, resp, body) {
                if (err) {

                    callback(err);
                    return;
                }
                callback(null, body);
            });
            break;
    }
}

var apicaller = new apiService();
var url = "http://google.com";
var called = 0;
var cb = function() {
    console.log("called once" + (called++));
}
console.log("calling");
apicaller.requestAPI(null, null, cb, url, "GET");
console.log("calling");
apicaller.requestAPI(null, null, cb, url, "GET");
console.log("calling");
apicaller.requestAPI(null, null, cb, url, "GET");
console.log("calling");
apicaller.requestAPI(null, null, cb, url, "GET");
console.log("calling");
apicaller.requestAPI(null, null, cb, url, "GET");

Upvotes: 1

Related Questions