Кластер узлов не порождает фантомные экземпляры в правильном рабочем потоке

Я использую NodeJS с PhantomJS. Моя цель — создать экземпляры узлов 4x с кластером узлов, каждый с двумя фантомными дочерними элементами. И мой код выглядит так:

кластер.js:

var numCPUs = 4;

if (cluster.isMaster) {

    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
        cluster.fork();
    });

} else {
    require("./app");
}

App.js выглядит так:

var instances = [];
var phantom = require('phantom');

function InstanceManager(instCount) {
    for (var i = 0; i < instCount; i++) {
           phantom.create(function(phantomInstance) {
            instances.push({
                cycle: 0,
                locked: false,
                instance: phantomInstance
            });
        });
    }
}

InstanceManager(2);

setInterval(function() {
    var i = 0;
    console.log('--' + instances.length);
}, 5000);

Итак, после запуска cluster.js ожидаемый вывод в консоли узла каждые 5 секунд должен быть:

--2
--2
--2
--2

но вместо этого выглядит так:

--0
--0
--0
--8

Почему фантомные экземпляры не привязаны к правильному воркеру?


person Deepsy    schedule 12.09.2014    source источник
comment
Что произойдет, если вы увеличите время ожидания setInterval до 10 000 или 15 000? Запуск 2 экземпляров может быть слишком медленным в течение 5 секунд или даже 10 секунд.   -  person Artjom B.    schedule 12.09.2014
comment
Все тот же результат :(   -  person Deepsy    schedule 12.09.2014
comment
Я запускаю узел 0.10.26, и он работает   -  person Maroshii    schedule 12.09.2014
comment
@Deepsy Интересная проблема!!! Я завершил свой ответ сегодня, это ниже. Мне пришлось опустить детали, но я надеюсь, что это достаточно ясно.   -  person idrarig    schedule 17.09.2014


Ответы (1)


Проблема, кажется, с фантомным модулем, неправильно работающим с кластером. Если вы замените его чем-то вроде тестового двойника, например

var phantom = {
    create: function (callback) {
        setImmediate(callback);
    }
};

вы получаете ожидаемый результат all-2. Чтобы продолжить свое расследование, я изменил node_modules/phantom/phantom.js, чтобы получить минимальную настройку, где возникает ваша проблема. Это оно:

var http = require('http'), shoe = require('shoe'), spawn = require('win-spawn');

exports.create = function(cb) {
    var httpServer, sock;
    httpServer = http.createServer();
    httpServer.listen(0);
    httpServer.on('listening', function() {
        var listeningPort = httpServer.address().port;
        spawn('phantomjs', [].concat([__dirname + '/shim.js', listeningPort]));
    });
    sock = shoe(cb);
    return sock.install(httpServer, '/dnode');
};

Здесь происходит следующее: запускается сервер прослушивания, затем запускается процесс phantomjs, который подключается к серверу прослушивания через WebSocket и пишет в него, после чего вызывается callback cb(). Вы можете понять это, взглянув на shim.js и немного поэкспериментировав.

В чем тогда проблема!? Ну, если вы console.log() listeningPort вы увидите, что вы получаете один и тот же порт 8 раз. Таким образом, получается, что каждый раз, когда вы вызываете phantom.create(), вы каким-то образом повторно используете один и тот же прослушивающий сервер, поэтому ваш обратный вызов вызывается только в одном процессе.

Похоже, это своеобразное поведение используемой вами версии Node при попытке прослушивания порта 0. Это также объясняет, почему с другой версией Node проблема не возникает (согласно комментарию выше). Вот моя суть, которая изолирует это нелогичное поведение.

Решение состоит в том, чтобы указать порт при вызове phantom.create() и использовать 8 разных портов, например, phantom.create(fn, { port: YOUR_PORT }) в вашем app.js.

person idrarig    schedule 15.09.2014
comment
Превосходно! Ты настоящий MVP! - person Deepsy; 18.09.2014