在服务器上调用 Collection.insert 时,“流星代码必须始终在 Fiber 内运行”
Posted
技术标签:
【中文标题】在服务器上调用 Collection.insert 时,“流星代码必须始终在 Fiber 内运行”【英文标题】:"Meteor code must always run within a Fiber" when calling Collection.insert on server 【发布时间】:2012-04-28 21:26:32 【问题描述】:我在 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
我不太清楚这个错误是什么意思。有没有人有任何想法,或者可以提出不同的方法?
【问题讨论】:
我应该明确指出,“服务”已在单独文件的其他地方定义(客户端和服务器通用)。 这一行是问题所在:Services.insert(host: host, service: service, hardState: hardState, currState: currState);
我认为是因为它在回调中,目前无法为您测试。
Meteor 现在包含一个 HTTP 请求库,可以让您的案例变得更容易:docs.meteor.com/#meteor_http
【参考方案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();
我刚刚遇到了类似的问题,这对我有用。不过我要说的是,我对此很陌生,我不知道这是否应该这样做。
您可能只需将插入语句包装在 Fiber 中就可以逃脱,但我并不肯定。
【讨论】:
【参考方案2】:根据我的测试,您必须将插入包装在我测试过的代码中,类似于上面的示例。
例如,我这样做了,但它仍然因 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 中。这可能也值得测试。就我而言,我需要在执行本地操作之前知道远程操作发生了。
希望这有助于这个问题线程。
【讨论】:
仅将函数包装在 Fiber 中可能还不够,并且可能导致意外行为。
原因是,与 Fiber 一起,Meteor 需要一组附加到 Fiber 的变量。 Meteor 使用附加到光纤的数据作为动态范围,将其与 3rd 方 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
【讨论】:
这对我有用。接受的答案无效,因为Fiber
不存在!
Fibers只存在于服务端,你是放在公共代码还是客户端代码中?
嵌套回调呢?例如 1-(readFile) 2-(将文件上传到 s3) 3-(然后将返回的 url 保存到 db)。我们需要在每个回调上使用 bindEnvironment 还是只在第一个回调上使用?以上是关于在服务器上调用 Collection.insert 时,“流星代码必须始终在 Fiber 内运行”的主要内容,如果未能解决你的问题,请参考以下文章