在服务器上调用 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 中。这可能也值得测试。就我而言,我需要在执行本地操作之前知道远程操作发生了。

希望这有助于这个问题线程。

【讨论】:

[02:33:57] 这是一个糟糕的答案,我应该对此发表评论。 [02:34:20] 确实创建了一个 Fiber,但它没有在其中设置有用的流星上下文,它实际上并没有阻塞内部线程上的外部方法。 @TomWijsman 那么哪个准确呢?在方法中围绕整个代码块进行纤维化? [01:40:21] 1:解开写栅栏。有关此示例,请参阅 packages/meteor/timers.js 中 Meteor.setTimeout 的实现。 [01:41:19] 2:将您定义的任何回调包装在一个新的纤程中,以便该方法在您的回调运行之前不会返回。这就是同步流星 API 的实现方式,例如在 packages/mongo-livedata/mongo_driver.js [01:42:02] 中,正确的答案实际上是 3:骂我们没有为你实现一个像样的同步 API重新尝试做:) 不是 deberg 的话: 应该注意的是,#2 需要使用来自 node-fibers 的 FUTURES,因此它与您的方法有点不同。在任何情况下,虽然这可能适用于您的特定用途,但它可能不是正确的方法,并且对每个人都不可靠。我们真的应该让#3 工作...... @TomWijsman 我明白了。顺便说一句,在这个线程上使用 Fibers 的其他版本与我所做的和工作类似。他们也需要#3?【参考方案3】:

仅将函数包装在 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 内运行”的主要内容,如果未能解决你的问题,请参考以下文章

文档的增删改查

mongodb3.2系统性学习——1文档插入insert insertOne insertMany

MongoDB 基础增删改查

Python语言学习 1.2

MongoDB对文档的操作

Mongodb学习总结