[英]REST API with Node.js failing at simultaneous requests to itself
I made a REST API service using node.js and it works perfectly, until I open a couple of tabs in my browser and make a couple of requests (almost) at the same time. 我使用node.js提供了REST API服务,并且该服务运行良好,直到在浏览器中打开几个标签并同时(几乎)发出几个请求。 The last tab that sends the request gets the response and the other one hangs.
发送请求的最后一个选项卡获取响应,另一个挂起。
Basically I have a module called "service" which does all the work: 基本上,我有一个名为“服务”的模块,可以完成所有工作:
var service = require('./service');
var server = http.createServer(function(req, res) {
...
var serviceResult = service.runService(parsedURL.query.username, parsedURL.query.api_key);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'keep-alive' });
service.ready = function(serviceResult) {
var serviceResultJSON = JSON.stringify(serviceResult);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'close' });
res.end(serviceResultJSON);
}
}
And from the service module I call: 从服务模块调用:
service.ready(result);
... whenever the result is ready and that's my server in a nutshell. ...只要结果准备就绪,那简直就是我的服务器。 So what can I do fix the issue?
那我该怎么解决这个问题呢?
EDIT: 编辑:
Here is what my service.js module looks like (after the suggested changes): 这是我的service.js模块的外观(在建议的更改之后):
// PUBLIC
exports.runService = function(username, apiKey, callback) {
_.username = username;
_.apiKey = apiKey;
init();
userManager.setLastServiceGlobal(function() {
// This call triggers the whole cycle. Below is snapshotManager.createdSnapshot(), which gets executed from within snapshotManager and the cycle moves on until apiManager.ready() gets called from within api-manager.js
snapshotManager.createSnapshot(false);
});
// This is the last .ready() function that gets executed when all other modules have finished their job.
apiManager.ready = function() {
console.log('API Manager ready.');
userManager.updateLastService();
callback(null, serviceResult);
}
}
// PRIVATE
var userManager = require('./user-manager'),
productManager = require('./product-manager'),
commentsManager = require('./comments-manager'),
apiManager = require('./api-manager'),
milestonesManager = require('./milestones-manager'),
statisticsManager = require('./statistics-manager'),
snapshotManager = require('./snapshot-manager'),
serviceResult;
...
snapshotManager.createdSnapshot = function() {
userManager.refreshUserData();
}
snapshotManager.createdNewSnapshot = function() {
milestonesManager.generateMilestones();
}
userManager.refreshedUserData = function() {
userManager.isTimeForPortfolioParse();
}
...
userManager.ready = function() {
console.log('User Manager ready.');
userManagerReady = true;
isDataFetchingOver();
}
productManager.ready = function() {
console.log('Product Manager ready.');
productManagerReady = true;
isDataFetchingOver();
}
commentsManager.ready = function() {
console.log('Comments Manager ready.');
commentsManagerReady = true;
isDataFetchingOver();
}
In this situation the modules are getting overridden just like the "service" module in the "server.js" file, correct? 在这种情况下,这些模块就像“ server.js”文件中的“ service”模块一样被覆盖,对吗? I'm guessing that I need to implement callbacks everywhere instead of the .ready() functions, correct?
我猜想我需要在各处实现回调,而不是.ready()函数,对吗?
There are three problems here! 这里有三个问题!
ready
property on service
, while there's only one service
object. service
上设置ready
属性,而只有一个service
对象。 If two requests happen very near to each other, you might end up overwriting the ready
property with the second request before it's been fired for the first request. ready
属性。 This is because there's only one instance of the service
object. service
对象只有一个实例。 That's almost definitely the way it should be, so don't worry about that, but rather we have to find a new way to signal to your consuming code that the service action has completed. res
variable inside service.read
is probably not the same res
that you think it is. res
里面的变量service.read
可能是不一样的res
,你认为它是。 The second time it gets called, it'll be different. ready
property, so it's getting a different scope every time. ready
属性,因此每次都会获得不同的作用域。 To fix the first problem, I suggest that you use callbacks! 要解决第一个问题,建议您使用回调! You should be familiar with them already from the node core, they're used for just about everything.
您应该已经从节点核心熟悉它们,它们几乎用于所有内容。 This also happens to fix the second problem, by virtue of scope in JavaScript.
借助于JavaScript的作用域 ,这也恰好解决了第二个问题。
Note: the reason I suggest using callbacks instead of a factory or some other method is that callbacks are pervasive in node-land, so if you use them, there's a good chance you'll have a much easier time integrating with other libraries and such. 注意:我建议使用回调代替工厂或其他方法的原因是,回调在节点领域普遍存在,因此,如果使用它们,则很有可能会更轻松地与其他库集成,例如。
Here's how I'd do your server code: 这是我处理服务器代码的方法:
var service = require('./service');
var server = http.createServer(function(req, res) {
// note that we're not immediately sending headers anymore - this
// fixes the third problem with what you had before
service.runService(parsedURL.query.username, parsedURL.query.api_key, function(err, serviceResult) {
// check for errors! tell the client!
if (err) {
res.writeHead(500);
res.end();
return;
}
res.writeHead(200, {
'content-type': 'application/json',
'connection' : 'keep-alive',
});
var serviceResultJSON = JSON.stringify(serviceResult);
res.end(serviceResultJSON);
});
};
And here's how I'd implement the service
thing: 这就是我实现
service
的方式:
var service = module.exports = {};
service.runService = function runService(username, key, cb) {
// assume `database` exists from somewhere else...
database.getUser(username, function(err, user) {
// make sure we send any errors up the line
if (err) {
cb(err);
return;
}
// here's an error we've decided on
if (user.key !== key) {
cb(Error("key is incorrect!"));
return;
}
// this is a very common usage pattern - cb(error, result, ...)
// the reason we're calling this will `null` for the error is a bit
// of a double negative - we're saying "there was no error".
cb(null, user);
});
};
The problem is they all share the same service object. 问题在于它们都共享相同的服务对象。 So what happens is that when you receive a request you overwrite service.ready to a function that responds to that request.
因此,发生的事情是,当您收到请求时,您将覆盖service.ready,准备好响应该请求的函数。 So if you get 2 requests really close together, service.ready will be set to respond to the last request you got and so, only that one will get a response.
因此,如果您真的有2个请求并排在一起,则将service.ready设置为响应上一个收到的请求,因此,只有一个将得到响应。
The best way is to have the service module export a function that returns an instance of service like this: 最好的方法是让服务模块导出一个返回如下服务实例的函数:
function serviceFactory() {
}
serviceFactory.prototype.getService() {
return new Service();
}
module.exports = serviceFactory;
And then you can have 然后你可以
var serviceFactory = require(./service);
var server = http.createServer(function(req, res) {
...
var service = serviceFactory.getService();
var serviceResult = service.runService(parsedURL.query.username, parsedURL.query.api_key);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'keep-alive' });
service.ready = function(serviceResult) {
var serviceResultJSON = JSON.stringify(serviceResult);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'close' });
res.end(serviceResultJSON);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.