Выполнить код в node.js из дочернего процесса

1

new to node.js Я пытаюсь выполнить некоторый код в дочернем процессе из модуля.

Позвольте мне объяснить, у меня есть простой сервер в index.js

var server = http.createServer(handleRequest);

server.listen(PORT, function(){
    console.log("Server listening on: http://localhost:%s", PORT);
});

Когда вызывается URL-адрес, я запускаю дочерний процесс:

function handleRequest(req, res){  
    console.log('Request path = ' + req.url)
    launchWorker(req.url)
    res.end('Path Hit: ' + req.url);
}

// workers execution 
function launchWorker(path) {
    const worker = child_process.spawn('node', ['./worker.js', path.substring(1)])

    worker.stdout.on('data', function(data) {
        console.log('worker: ' + data.toString())
    }); 

    worker.stderr.on('data', function(data) {
        console.log('stderr : ' + data)
    }); 

    worker.on('close', function(code, signal) {
        console.log('child process exited with code: ' + code)
    }); 
}  

Таким образом, у меня есть файл worker.js, который выполняет некоторый код, мне удалось передать некоторые данные с моего сервера на мой дочерний процесс, используя ['./worker.js', path.substring(1)]

Я использую firebase для выполнения некоторых операций, поэтому в моем worker.js я инициализирую свой администратор firebase, учетные данные и базу данных:

var admin = require("firebase-admin");

var serviceAccount = require("./service_account.json");

var defaultApp = admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "http://myAdress.firebase"
});

var db = admin.database()

performWork()

function performWork() {

    var arg = process.argv[2]
    var ref = db.ref("something");
    ref.once("value", function(snapshot) {
      console.log(arg);
      process.exit(1); 
    });

}

Я собираюсь запустить процесс каждую минуту, поэтому я хочу, чтобы дать моему ребенку базу данных firebase в качестве аргумента, поэтому я не создаю доступ к firebase при каждом запуске процесса. Так вот что я пытался сделать в index.js:

var admin = require("firebase-admin");

var serviceAccount = require("./service_account.json");

var defaultApp = admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "http://myAdress.firebase"
});

var db = admin.database()

...

function launchWorker(path) {
    const worker = child_process.spawn('node', ['./worker.js', path.substring(1), admin.database()])

Я просто передаю базу данных в качестве аргумента для моего работника. Тогда на моего работника я получаю fb:

function performWork() {

    var arg = process.argv[2]
    var db = process.argv[3]
    var ref = db.ref("something");
    ref.once("value", function(snapshot) {
      console.log(arg);
      process.exit(1); 
    });

}

Но когда я достиг URL-адреса, я получаю сообщение об ошибке:

TypeError: db.ref is not a function
    at performWork (/home/user/worker.js:12:15)
    at Object.<anonymous> (/home/user/worker.js:6:1)
    at Module._compile (module.js:635:30)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Function.Module.runMain (module.js:676:10)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3

Я думаю, что worker.js вообще не знает firebase и не может достичь функции.ref. даже если db не является нулевым (я могу напечатать его значение). Итак, мой вопрос заключается в том, что мне нужно импортировать/делать в work.js, чтобы он мог выполнять код из модуля firebase? Я стараюсь, чтобы пользователь требовал... но ничего не работает. Мой вопрос также более общий, я хотел бы написать некоторые помощники (создать, прочитать, удалить) в другом файле, но я получу ту же проблему. Спасибо за любую помощь.

  • 0
    Как вы думаете, почему необходимо использовать дочерние процессы? Я не вижу ничего, что указывало бы на то, что они необходимы, и использование дочерних процессов сопряжено с немалыми потерями производительности.
  • 0
    Мне нужно использовать дочерний процесс, чтобы освободить память, это тяжелая задача, которая вызывается чаще, это единственный способ, который я нашел, чтобы действительно освободить память, когда процессы завершены.
Теги:
firebase
firebase-realtime-database

1 ответ

1
Лучший ответ

Вы не можете передать живой объект Javascript другому процессу. Если объект содержит ТОЛЬКО данные Javascript, вы можете сериализовать его с помощью JSON и передать его другому процессу, где копия будет десериализована, но это не будет работать для объекта, у которого есть собственный код или TCP-соединение за ним, что, вероятно, что представляет собой ваш открытый дескриптор базы данных. Существуют некоторые обстоятельства, при которых вы можете передать TCP-соединение другому процессу (кластеризация делает это), но если вы его завернули в другой объект соединения (который обычно есть), то это куча работы по восстановлению объекта живого соединения с переданное TCP-соединение в нем в другом процессе. Лично я предпочел бы избежать всех этих хлопот.

Если у вас нет тяжелой работы с процессором в вашем дочернем процессе, я действительно не вижу причин, по которым здесь нужен детский процесс. Вы можете просто выполнять любую работу с базой данных в основном процессе, когда приходит запрос. Работа с базами данных должна быть в основном асинхронной и, следовательно, не должна существенно блокировать поток основных узлов.js, и вы должны иметь возможность иметь несколько запросов в полете по в то же время. С большим количеством запросов ваша база данных будет узким местом в любом случае, а не основным процессом node.js.

Если у вас есть законная причина использовать дочерние процессы, и вы не хотите открывать и закрывать новое соединение с БД для каждого из них, то вы, вероятно, захотите создать кучу рабочих процессов, которые вы открываете, и они остаются открытыми. Затем они могут открыть свое собственное подключение к базе данных и сохранить его открытым. Когда новый запрос поступает в ваш основной процесс node.js, вы используете некоторую форму IPC (возможно, просто записываете в stdin дочернего процесса), чтобы отправить ему новое задание, которое он просто сидит там, ожидая обработки. Вы можете оптимизировать ситуацию, решив, сколько рабочих процессов вам необходимо оптимизировать для пропускной способности. Затем, когда приходит новое задание, вы просто бросаете задание в очередь. Если есть свободные работники, вы отправляете следующее задание в очередь этому работнику. Каждый раз, когда работник заканчивает работу, вы проверяете, есть ли в очереди другой элемент для отправки этого работника.

Таким образом, вы не создаете и не убиваете новые соединения с вашей базой данных все время, и каждый рабочий процесс может иметь и поддерживать собственное соединение с базой данных.

  • 0
    Что ж, спасибо за объяснение, да у меня огромное количество процессов (раз в 30 секунд, раз в минуту, раз в 5, раз в 10 ...), работающих с большим количеством данных, у меня есть 16 ГБ ram, это единственный способ освободить память, когда дочерний процесс завершается, все освобождается, мой сервер может запустить этот процесс в течение месяца без перезагрузки из-за ошибки нехватки памяти. Благодарю.
  • 0
    Да, спасибо мужчина.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню