Вопрос по javascript – «Метеоритный код всегда должен выполняться внутри оптоволокна» при вызове Collection.insert на сервере

38

У меня есть следующий код в server / statusboard.js;

<code>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);
});
</code>

По сути, он извлекает некоторые данные из фида JSON и пытается поместить их в коллекцию.

Когда я запускаю Meteor, я получаю следующее исключение;

<code>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
</code>

Я не очень уверен, что означает эта ошибка. У кого-нибудь есть идеи или можно предложить другой подход?

Я должен дать понять, что & quot; Услуги & quot; был определен в другом месте в отдельном файле (общий для клиента и сервера). Andrew Beresford
Meteor теперь включает библиотеку HTTP-запросов, которая значительно упрощает ваше рассмотрение:docs.meteor.com/#meteor_http debergalis
Эта строка является проблемой:Services.insert({host: host, service: service, hardState: hardState, currState: currState});  Я думаю, потому что это в обратном вызове, нет способа проверить в данный момент для вас. jonathanKingston

Ваш Ответ

3   ответа
48

что может привести к неожиданному поведению.

Причина в том, что, наряду с Fiber, 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

Это помогло мне. Принятый ответ не сработал, потому чтоFiber не существует!
Это должен быть принятый ответ
Волокна существуют только на стороне сервера. Вы поместили их в общий или клиентский код?
как насчет вложенных обратных вызовов? например, 1- (readFile) 2- (загрузить файл в s3) 3- (затем сохранить возвращенный URL в db). Нужно ли использовать bindEnvironment для каждого обратного вызова или только для первого?
7

вы должны заключить вставку в код, который я тестировал, аналогично приведенному выше примеру.

Например, я сделал это, и это все еще не удалось с ошибкой 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. Это также может стоить проверить. В моем случае мне нужно было знать, что удаленное действие произошло до того, как я выполню свое локальное действие.

Надеюсь, что это способствует этой ветке вопроса.

@ TomWijsman, что тогда точно? Волокно вокруг всего блока кода в методе?
@ TomWijsman я вижу. Кстати, другие версии использования Fibers в этой теме похожи на то, что я делал и работаю. Им тоже нужен № 3?
& Lt; deberg & GT; [02:33:57] это плохой ответ, я должен это прокомментировать. & Lt; deberg & GT; [02:34:20] который создает волокно, но не устанавливает полезный метеорный контекст внутри него, и он фактически не блокирует внешний метод во внутреннем потоке.
Not deberg's words: Следует отметить, что # 2 требовал использования FUTURES от узловых волокон, поэтому он немного отличается от вашего подхода. В любом случае, хотя это может работать в вашем конкретном случае, это может быть неправильным способом и работать не надежно для всех. Мы должны действительно заставить № 3 работать ...
& Lt; deberg & GT; [01:40:21] 1: отцепить забор записи. посмотрите пример реализации Meteor.setTimeout в пакетах / meteor / timers.js. & Lt; deberg & GT; [01:41:19] 2: обернуть любой обратный вызов, который вы определили в новом волокне, чтобы метод не возвращался до тех пор, пока ваш обратный вызов не запустится. это то, как реализованы синхронные метеорологические API, как в пакетах / mongo-aliveata / mongo_driver.js & lt; deberg & gt; [01:42:02] правильный ответ на самом деле 3: кричите на нас за то, что мы не внедрили достойный синхронный API для всего, что вы пытаетесь сделать :)
15

это потому, что вы выполняете код в обратном вызове.

Любой код, который вы выполняете на стороне сервера, должен содержаться в волокне.

Попробуйте изменить функцию 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, но я не уверен.

Похожие вопросы