【问题标题】:Cannot place multiple GET requests on nodejs servers无法在 nodejs 服务器上放置多个 GET 请求
【发布时间】:2016-04-27 03:17:11
【问题描述】:

我有 2 台相互通信的服务器。当用户想要检查传感器的健康状况时,它会点击 API /check/health,然后与监控服务器对话以获取传感器的状态字段。此状态被发送回 Provisioning 并发送给用户。

我对此进行了测试,它运行良好..即当我执行http://localhost:3000/check/health/4(3 是传感器 ID)时,我得到的响应为 Good。当我执行另一个请求时说http://localhost:3000/check/health/3,我在配置服务器上收到如下错误:

有谁知道为什么我不能做第二个 GET 请求。我似乎让它只适用于第一次健康检查而不是后续健康检查。

输出/错误:

服务器监听 3000 端口
已连接到监控服务器!
服务器获得了传感器 ID 的运行状况检查:4
传感器状态:良好
服务器获得了传感器 ID 的运行状况检查:3
传感器的状态是:未定义
_http_outgoing.js:344
throw new Error('Can\'t set headers after they are sent.');
^

错误:发送后无法设置标头。
在 ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)

供应服务器:

// Code to communicate with Monitoring server
var io = require('socket.io-client');
var socket = io.connect('http://localhost:4000', { reconnect: true });
socket.on('connect', function(socket) {
  console.log('Connected to Monitoring Server!');
});

// Code to serve the Sensor users
var express = require('express');
var app= express();
var path = require('path');
var bodyParser= require('body-parser');

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/sensor_db');
var Schema = mongoose.Schema;

var sensorSchema = new Schema({
  value:{ type:Number, default:0},
  format:{type:String, default:"default"},
  id:{type:Number,required:true,unique:true},
  description:{type:String},
  type:{type:String},
  groupId:{type:Number},
  users:{type:Array,default:[]},
  admin:{type:String,default:'Undefined'},
  status:{type:String,default:'InActive'},
  owner:{type:String,default:'Undefined'},
  templateId:{type:Number}

});
var Sensor = mongoose.model('Sensor',sensorSchema);

// API to get the health of the Sensor by looking up its Status, pass the Sensor ID to check
app.get('/check/health/:id', function (req, res) {

  var id = req.params.id;
  console.log("Server got a health check for Sensor ID :", id);
  socket.emit('eventToClient',{ id: id })
  socket.on('reply', function (data) {
    console.log("The Status of the Sensor is :",data.status);
    res.json(data.status);
  });
})

app.listen(3000);
console.log('Server listening at port 3000');

监控服务器:

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var express = require('express');

var mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/sensor_db') ;
var Schema = mongoose.Schema;

var sensorSchema = new Schema({
  value:{ type:Number, default:0},
  format:{type:String, default:"default"},
  id:{type:Number,required:true,unique:true},
  description:{type:String},
  type:{type:String},
  groupId:{type:Number},
  users:{type:Array,default:[]},
  admin:{type:String,default:'Undefined'},
  status:{type:String,default:'Undefined'},
  owner:{type:String,default:'Undefined'},
  templateId:{type:Number}

});
var Sensor = mongoose.model('Sensor',sensorSchema);

io.on('connection', function(socket){
  console.log('connection received from Provisioning');
  // To get messages from Provisioning server
  socket.on('eventToClient',function(data) {
    var id = data.id
    Sensor.findOne({id: id}, function (err, doc) {
      if (err) return res.status(500).send({error: err});
      socket.emit('reply',doc)

    })
  });
});

server.listen(4000, function(){
  console.log('socket.io server listening on *:4000');
});

【问题讨论】:

  • 你每次都面临同样的错误吗?
  • 是的,我每次都看到
  • 在监控服务器中,你能解释一下你从哪里得到res in if (err) return res.status(500).send({error: err}); 吗?
  • 感谢您发现,这是一个错误,但我猜这个问题不是这样?

标签: javascript node.js websocket socket.io


【解决方案1】:

我认为 res 变量正在失去其上下文。因为socket.on('eventToClient',Fn) 是一个事件侦听器,仅在触发特定事件时才会触发。这是一个成功的回调类型函数。就好像您将 socket.on('eventToClient',Fn) 也放在 get 端点之外,它仍然会被解雇。仅在附加事件侦听器时才使用 res 上下文。因此,一旦为第一个请求发送 res,下次上下文就过期了。

更新::检查以下代码更改

供应服务器:

// Code to communicate with Monitoring server
var io = require('socket.io-client');
var socket = io.connect('http://localhost:4000', { reconnect: true });
socket.on('connect', function(socket) {
  console.log('Connected to Monitoring Server!');
});

// Code to serve the Sensor users
var express = require('express');
var app= express();
var path = require('path');
var bodyParser= require('body-parser');

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/sensor_db');
var Schema = mongoose.Schema;

var sensorSchema = new Schema({
  value:{ type:Number, default:0},
  format:{type:String, default:"default"},
  id:{type:Number,required:true,unique:true},
  description:{type:String},
  type:{type:String},
  groupId:{type:Number},
  users:{type:Array,default:[]},
  admin:{type:String,default:'Undefined'},
  status:{type:String,default:'InActive'},
  owner:{type:String,default:'Undefined'},
  templateId:{type:Number}

});
var Sensor = mongoose.model('Sensor',sensorSchema);
var healthResObj = {};

socket.on('reply', function (data) {
    console.log("The Status of the Sensor is :", data.result.status);
    healthResObj[data.resKey].json(data.result.status); // get the response object from by providing its key
 delete healthResObj[data.resKey]; //remove the object from memory
  });

// API to get the health of the Sensor by looking up its Status, pass the Sensor ID to check
app.get('/check/health/:id', function (req, res) {

  var id = req.params.id;
  console.log("Server got a health check for Sensor ID :", id);
  socket.emit('eventToClient',{ id: id })
  var key = Date.now();
  healthResObj[key] = res;
  socket.emit('eventToClient',{ id: id , resKey: key}); // pass the key where the response object stored so that can be retrieved later 
})

app.listen(3000);
console.log('Server listening at port 3000');

监控服务器:

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var express = require('express');

var mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/sensor_db') ;
var Schema = mongoose.Schema;

var sensorSchema = new Schema({
  value:{ type:Number, default:0},
  format:{type:String, default:"default"},
  id:{type:Number,required:true,unique:true},
  description:{type:String},
  type:{type:String},
  groupId:{type:Number},
  users:{type:Array,default:[]},
  admin:{type:String,default:'Undefined'},
  status:{type:String,default:'Undefined'},
  owner:{type:String,default:'Undefined'},
  templateId:{type:Number}

});
var Sensor = mongoose.model('Sensor',sensorSchema);

io.on('connection', function(socket){
  console.log('connection received from Provisioning');
  // To get messages from Provisioning server
  socket.on('eventToClient',function(data) {
    var id = data.id
    Sensor.findOne({id: id}, function (err, doc) {
      if (err) return res.status(500).send({error: err});
      socket.emit('reply',{result:doc, resKey: data.resKey}); //forwarding the resKey( the key of the response);

    })
  });
});

server.listen(4000, function(){
  console.log('socket.io server listening on *:4000');
});

【讨论】:

  • 感谢基肖尔!你介意是否可以键入所需的代码,因为我不完全理解:(它需要更改的这段代码 - app.get('/check/health/:id', function (req, res) { var id = req.params.id; console.log("服务器对 Sensor ID 进行了健康检查:", id); socket.emit('eventToClient',{ id: id }) socket.on('reply', function (data) { console.log("传感器的状态是:",data.status); res.json(data.status); }); })
  • 是的,我可以有.. 但现在我不能 :( 可能会晚点
  • 感谢基肖尔!我试过你的代码..我在供应端收到这个错误
  • 服务器监听端口 3000 连接到监控服务器!服务器对传感器 ID 进行了健康检查:4 传感器的状态是:良好 c:\Users\jessig\WebstormProjects\Provisioning_server\app.js:227 healthResObj[data.resKey].json(data.result.status); // 通过提供其键来获取响应对象 ^
  • 这一行出错:healthResObj[data.resKey].json(data.result.status); // 从provid获取响应对象
【解决方案2】:

您遇到此问题是因为当您访问/check/health/3 时,它会发出eventToClient 事件并等待来自监控服务器的reply 事件。但是,在等待回复的同时,端点退出。

一段时间后,当您收到reply 事件时,您会尝试发送res.json。但是,res 已经结束了。这就是您收到 Can't set headers after they are sent. 错误的原因。

【讨论】:

  • 我认为不应该这样,因为 js 是异步的。假设我们将 DB 读取逻辑放在端点本身,那么它应该等到我们从 DB 中获取结果,然后才能发送结果作为响应。
  • 对不起,我应该如何更改我的代码才能使其正常工作,非常感谢
  • 是的,DB 逻辑很耗时,延迟了进程,JS 是异步的。但是,事情就是这样发生的。
  • 跟回调有关系吗?
【解决方案3】:

您可以通过更新配置端点以始终返回 OK 但保留 socket.on('reply', function()) 来证明我的观点

app.get('/check/health/:id', function (req, res) {

  var id = req.params.id;
  console.log("Server got a health check for Sensor ID :", id);
  socket.emit('eventToClient',{ id: id })
  socket.on('reply', function (data) {
    console.log("The Status of the Sensor is :",data.status);
  });
  res.json("ok")
})

您将首先看到来自“回复”的 1 条回复,然后是 2 条……然后是 3 条

Server got a health check for Sensor ID : 3
The Status of the Sensor is : Good
Server got a health check for Sensor ID : 3
The Status of the Sensor is : Good
The Status of the Sensor is : Good
Server got a health check for Sensor ID : 3
The Status of the Sensor is : Good
The Status of the Sensor is : Good
The Status of the Sensor is : Good

【讨论】:

    【解决方案4】:

    这是一个有趣的问题。

    当您点击供应服务器上的端点 /check/health/:id 时,您每次都创建一个新的套接字事件侦听器。

    它第一次起作用是因为只有一个侦听器在期待“回复”——但下一次你到达端点时,有两个相互竞争的侦听器。第一个侦听器发送响应,在您收到该响应之前,第二个侦听器已尝试设置标头并出错。

    【讨论】:

    • 不,它不是每次都创建新的套接字事件监听器。套接字连接只进行一次,
    • @user3384225 :注册您的评论:第一个侦听器发送响应,在您收到该响应之前,第二个侦听器已尝试设置标头并出错。仅在收到响应后,我发出第二个请求,即收到错误消息。所以他们没有竞争
    • @JessiAnnGeorge no in nodejs 如果您发送请求 100 背靠背请求,它也会相应地异步处理它们。只是当 reply 事件被触发时它没有得到 res 上下文。套接字不像你调用的函数,它的成功是有价值的。
    • 啊,是的,很抱歉不清楚,第一个请求响应不与第二个竞争。但是当您发出第二个请求(第一个成功完成)时,我们创建另一个事件侦听器,它将从监控服务器发送响应,这两个侦听器将竞争并生成错误,因为它们都尝试设置标头。
    • 另外,我认为每次发出请求时都附加事件侦听器不是一个好主意。事件侦听器应附加一次。如果您也将socket.on('reply', function (data) { console.log("The Status of the Sensor is :",data.status); }); 放在 get 端点之外,仍然可以从监控服务器多次调用它。这只是我们必须注意的 res 变量。
    猜你喜欢
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-22
    • 1970-01-01
    相关资源
    最近更新 更多