"Код Meteor должен всегда запускаться внутри Fiber" при вызове Collection.insert на сервере
У меня есть следующий код в server/statusboard.js;
var require = __meteor_bootstrap__.require,
request = require("request")
function getServices(services) {
services = [];
request('http://some-server/vshell/index.php?type=services&mode=json', function (error, response, body) {
var resJSON = JSON.parse(body);
_.each(resJSON, function(data) {
var host = data["host_name"];
var service = data["service_description"];
var hardState = data["last_hard_state"];
var currState = data["current_state"];
services+={host: host, service: service, hardState: hardState, currState: currState};
Services.insert({host: host, service: service, hardState: hardState, currState: currState});
});
});
}
Meteor.startup(function () {
var services = [];
getServices(services);
console.log(services);
});
В основном, он извлекает некоторые данные из канала JSON и пытается вставить его в коллекцию.
Когда я запускаю Meteor, я получаю следующее исключение:
app/packages/livedata/livedata_server.js:781
throw exception;
^
Error: Meteor code must always run within a Fiber
at [object Object].withValue (app/packages/meteor/dynamics_nodejs.js:22:15)
at [object Object].apply (app/packages/livedata/livedata_server.js:767:45)
at [object Object].insert (app/packages/mongo-livedata/collection.js:199:21)
at app/server/statusboard.js:15:16
at Array.forEach (native)
at Function.<anonymous> (app/packages/underscore/underscore.js:76:11)
at Request._callback (app/server/statusboard.js:9:7)
at Request.callback (/usr/local/meteor/lib/node_modules/request/main.js:108:22)
at Request.<anonymous> (/usr/local/meteor/lib/node_modules/request/main.js:468:18)
at Request.emit (events.js:67:17)
Exited with code: 1
Я не слишком уверен, что означает эта ошибка. Кто-нибудь есть идеи или может предложить другой подход?
Ответы
Ответ 1
Как уже упоминалось выше, это потому, что ваш исполняемый код в обратном вызове.
Любой код, который вы используете на стороне сервера, должен содержаться внутри Fiber.
Попробуйте изменить функцию getServices таким образом:
function getServices(services) {
Fiber(function() {
services = [];
request('http://some-server/vshell/index.php?type=services&mode=json', function (error, response, body) {
var resJSON = JSON.parse(body);
_.each(resJSON, function(data) {
var host = data["host_name"];
var service = data["service_description"];
var hardState = data["last_hard_state"];
var currState = data["current_state"];
services+={host: host, service: service, hardState: hardState, currState: currState};
Services.insert({host: host, service: service, hardState: hardState, currState: currState});
});
});
}).run();
}
Я столкнулся с подобной проблемой, и это сработало для меня. Однако я должен сказать, что я очень новичок в этом, и я не знаю, так ли это должно быть сделано.
Вероятно, вам удастся только обернуть свою инструкцию insert в Fiber, но я не уверен.
Ответ 2
Просто обернуть вашу функцию в волокно может быть недостаточно и может привести к неожиданному поведению.
Причина в том, что наряду с Fiber, Meteor требует набор переменных, привязанных к волокну. Meteor использует данные, привязанные к волокну в качестве динамической области, и самый простой способ использовать его с сторонним api - использовать Meteor.bindEnvironment
.
T.post('someurl', Meteor.bindEnvironment(function (err, res) {
// do stuff
// can access Meteor.userId
// still have MongoDB write fence
}, function () { console.log('Failed to bind environment'); }));
Посмотрите эти видеоролики на случайный ум, если хотите узнать больше:
https://www.eventedmind.com/posts/meteor-dynamic-scoping-with-environment-variables
https://www.eventedmind.com/posts/meteor-what-is-meteor-bindenvironment
Ответ 3
Основываясь на моих тестах, вы должны обернуть вставку в тестируемый мной код, который аналогичен приведенному выше примеру.
Например, я сделал это, и он по-прежнему не прошел с ошибкой Fibers.
function insertPost(args) {
if(args) {
Fiber(function() {
post_text = args.text.slice(0,140);
T.post('statuses/update', { status: post_text },
function(err, reply) {
if(reply){
// TODO remove console output
console.log('reply: ' + JSON.stringify(reply,0,4));
console.log('incoming twitter string: ' + reply.id_str);
// TODO insert record
var ts = Date.now();
id = Posts.insert({
post: post_text,
twitter_id_str: reply.id_str,
created: ts
});
}else {
console.log('error: ' + JSON.stringify(err,0,4));
// TODO maybe store locally even though it failed on twitter
// and run service in background to push them later?
}
}
);
}).run();
}
}
Я сделал это, и он работал нормально, без ошибок.
function insertPost(args) {
if(args) {
post_text = args.text.slice(0,140);
T.post('statuses/update', { status: post_text },
function(err, reply) {
if(reply){
// TODO remove console output
console.log('reply: ' + JSON.stringify(reply,0,4));
console.log('incoming twitter string: ' + reply.id_str);
// TODO insert record
var ts = Date.now();
Fiber(function() {
id = Posts.insert({
post: post_text,
twitter_id_str: reply.id_str,
created: ts
});
}).run();
}else {
console.log('error: ' + JSON.stringify(err,0,4));
// TODO maybe store locally even though it failed on twitter
// and run service in background to push them later?
}
}
);
}
}
Я думал, что это может помочь другим столкнуться с этой проблемой. Я еще не тестировал вызов асинхронного типа внешней службы после внутреннего кода и обертывания в Fiber. Это может стоить и тестирование. В моем случае мне нужно было знать, что удаленное действие произошло до того, как я сделаю локальное действие.
Надеюсь, что это поможет в этом вопросе.