Express.js - 在发送响应之前循环
Express.js - while loop before sending response
我正在尝试在 node.js 中实施现有解决方案,特别是使用 express.js 框架。现在,现有解决方案的工作原理如下:
- 服务器公开了客户端可以连接到的 GET 服务
- 客户端调用GET服务时,客户端号递增(全局变量),然后检查客户端数;
- 如果连接的客户端少于3个,则服务死循环,等待其他客户端连接
- 如果(或者更确切地说,当)两个客户端的其余部分连接时,服务会向每个人发送响应,表明已连接足够多的客户端('true' 值)。
所以基本上发生的事情是,客户端连接并且连接处于活动状态(在循环中)直到有足够的客户端连接,然后并且只有在那时才会有响应(同时对所有客户端)。
现在我不是这些架构的专家,但在我看来,这不是一个正确或好的解决方案。我最初的想法是:这个必须用套接字来解决。然而,由于现有的解决方案是这样工作的(它不是用 node.js 编写的),我试图模仿这种行为:
var number = (function(){
var count = 0;
return {
increase: function() {
count++;
},
get: function(){
return count;
}
};
})();
app.get('/test', function(req, res){
number.increase();
while (number.get() < 3) {
//hold it here, until enough clients connect
}
res.json(number.get());
});
虽然我认为这不是一个正确的解决方案,但我有几个问题:
- 除了使用套接字之外,还有其他方法可以解决这个问题吗?
- 为什么这个 "logic" 在 C# 中有效,但在 express.js 中无效?上面的代码挂起,没有处理其他请求。
- 我知道 node.js 是单线程的,但是如果我们有一个更常规的立即响应的服务,并且同时有 20 个请求怎么办?
- 您的 http 方法应该有效
- 您正在阻塞事件循环,因此节点在 while 循环中拒绝做任何其他工作
- 你真的很接近,你只需要时不时地检查一下,而不是经常检查。我在下面使用
process.nextTick()
执行此操作,但 setTimeout()
也可以:
var number = (function(){
var count = 0;
return {
increase: function() {
count++;
},
get: function(){
return count;
}
};
})();
function waitFor3(callback){
var n = number.get();
if(n < 3){
setImmediate(function(){
waitFor3(callback)
})
} else {
callback(n)
}
}
function bump(){
number.increase();
console.log('waiting');
waitFor3(function(){
console.log('done');
})
}
setInterval(bump, 2000);
/*
app.get('/test', function(req, res){
number.increase();
waitFor3(function(){
res.json(number.get());
})
});
*/
我可能会为此使用事件发射器:
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
app.get('/', function(req, res) {
// Increase the number
number.increase();
// Get the current value
var current = number.get();
// If it's less than 3, wait for the event emitter to trigger.
if (current < 3) {
return emitter.once('got3', function() {
return res.json(number.get());
});
}
// If it's exactly 3, emit the event so we wake up other listeners.
if (current === 3) {
emitter.emit('got3');
}
// Fall through.
return res.json(current);
});
我想强调的是,@Plato 的说法是正确的,当响应花费太多时间才能完成时,浏览器可能会超时。
编辑:顺便说一句,关于return emitter.once(...)
的一些解释。
上面的代码可以这样重写:
if (current < 3) {
emitter.once('got3', function() {
res.json(number.get());
});
} else if (current === 3) {
emitter.emit('got3');
res.json(number.get());
} else {
res.json(number.get());
}
但是我没有使用那些 if/else
语句,而是在创建事件侦听器后从请求处理程序中 return。由于请求处理程序是异步的,因此它们的 return 值将被丢弃,因此您可以 return 任何东西(或什么都不做)。作为替代方案,我也可以使用这个:
if (current < 3) {
emitter.once(...);
return;
}
if (current === 3) {
...etc...
此外,即使您从请求处理程序函数 return,事件侦听器仍引用 res
变量,因此请求处理程序范围由 Node 维护,直到 res.json()
在事件侦听器回调中被调用。
我正在尝试在 node.js 中实施现有解决方案,特别是使用 express.js 框架。现在,现有解决方案的工作原理如下:
- 服务器公开了客户端可以连接到的 GET 服务
- 客户端调用GET服务时,客户端号递增(全局变量),然后检查客户端数;
- 如果连接的客户端少于3个,则服务死循环,等待其他客户端连接
- 如果(或者更确切地说,当)两个客户端的其余部分连接时,服务会向每个人发送响应,表明已连接足够多的客户端('true' 值)。
所以基本上发生的事情是,客户端连接并且连接处于活动状态(在循环中)直到有足够的客户端连接,然后并且只有在那时才会有响应(同时对所有客户端)。
现在我不是这些架构的专家,但在我看来,这不是一个正确或好的解决方案。我最初的想法是:这个必须用套接字来解决。然而,由于现有的解决方案是这样工作的(它不是用 node.js 编写的),我试图模仿这种行为:
var number = (function(){
var count = 0;
return {
increase: function() {
count++;
},
get: function(){
return count;
}
};
})();
app.get('/test', function(req, res){
number.increase();
while (number.get() < 3) {
//hold it here, until enough clients connect
}
res.json(number.get());
});
虽然我认为这不是一个正确的解决方案,但我有几个问题:
- 除了使用套接字之外,还有其他方法可以解决这个问题吗?
- 为什么这个 "logic" 在 C# 中有效,但在 express.js 中无效?上面的代码挂起,没有处理其他请求。
- 我知道 node.js 是单线程的,但是如果我们有一个更常规的立即响应的服务,并且同时有 20 个请求怎么办?
- 您的 http 方法应该有效
- 您正在阻塞事件循环,因此节点在 while 循环中拒绝做任何其他工作
- 你真的很接近,你只需要时不时地检查一下,而不是经常检查。我在下面使用
process.nextTick()
执行此操作,但setTimeout()
也可以:
var number = (function(){
var count = 0;
return {
increase: function() {
count++;
},
get: function(){
return count;
}
};
})();
function waitFor3(callback){
var n = number.get();
if(n < 3){
setImmediate(function(){
waitFor3(callback)
})
} else {
callback(n)
}
}
function bump(){
number.increase();
console.log('waiting');
waitFor3(function(){
console.log('done');
})
}
setInterval(bump, 2000);
/*
app.get('/test', function(req, res){
number.increase();
waitFor3(function(){
res.json(number.get());
})
});
*/
我可能会为此使用事件发射器:
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
app.get('/', function(req, res) {
// Increase the number
number.increase();
// Get the current value
var current = number.get();
// If it's less than 3, wait for the event emitter to trigger.
if (current < 3) {
return emitter.once('got3', function() {
return res.json(number.get());
});
}
// If it's exactly 3, emit the event so we wake up other listeners.
if (current === 3) {
emitter.emit('got3');
}
// Fall through.
return res.json(current);
});
我想强调的是,@Plato 的说法是正确的,当响应花费太多时间才能完成时,浏览器可能会超时。
编辑:顺便说一句,关于return emitter.once(...)
的一些解释。
上面的代码可以这样重写:
if (current < 3) {
emitter.once('got3', function() {
res.json(number.get());
});
} else if (current === 3) {
emitter.emit('got3');
res.json(number.get());
} else {
res.json(number.get());
}
但是我没有使用那些 if/else
语句,而是在创建事件侦听器后从请求处理程序中 return。由于请求处理程序是异步的,因此它们的 return 值将被丢弃,因此您可以 return 任何东西(或什么都不做)。作为替代方案,我也可以使用这个:
if (current < 3) {
emitter.once(...);
return;
}
if (current === 3) {
...etc...
此外,即使您从请求处理程序函数 return,事件侦听器仍引用 res
变量,因此请求处理程序范围由 Node 维护,直到 res.json()
在事件侦听器回调中被调用。