2015-11-20 2 views
0

Я довольно смущен тем, почему мое обещание блокирует запросы приложений узла.NodeJS обещание блокировки запросов

Вот мой упрощенный код:

var express = require('express');  
var someModule = require('somemodule'); 

app = express(); 

app.get('/', function (req, res) {   
    res.status(200).send('Main'); 
}); 

app.get('/status', function (req, res) { 
    res.status(200).send('Status'); 
});   

// Init Promise 
someModule.doSomething({}).then(function(){},function(){}, function(progress){ 
    console.log(progress);   
}); 

var server = app.listen(3000, function() { 
    var host = server.address().address; 
    var port = server.address().port; 

    console.log('Example app listening at http://%s:%s in %s environment',host, port, app.get('env')); 
}); 

И модуль:

var q = require('q'); 

function SomeModule(){ 
    this.doSomething = function(){ 
     return q.Promise(function(resolve, reject, notify){    
      for (var i=0;i<10000;i++){ 
       notify('Progress '+i); 
      } 
      resolve(); 
     }); 
    }   
} 

module.exports = SomeModule; 

Очевидно, что это очень упрощена. Функция обещания выполняет некоторую работу, которая занимает от 5 до 30 минут и должна запускаться только при запуске сервера. Существует NO async работа в этом обе стороны функция. Его просто много обработки данных, петли и т. Д.

Я не буду иметь возможность делать запросы сразу же. Так что я ожидаю, когда я запускаю сервер, я могу сразу перейти на 127.0.0.1:3000 и посмотреть Main и так же для любых других запросов.

В конце концов я хочу увидеть прогресс этой задачи, обратившись к /status, но я уверен, что смогу сделать эту работу, как только сервер будет работать, как ожидалось.

В тот момент, когда я открываю / не просто виснет, пока работа обещание заканчивается ..

Очевидно им делать что-то неправильно ...

+1

JS не многопоточный, так что если у вас нет каких-либо асинхронной задачи в рамках своего обещания, и эта часть нуждается в 30мин, чтобы закончить, то он будет блокировать ваш сервер для 30мин. –

+0

... и даже если у вас есть асинхронные задачи, если они связаны с процессором, они все равно будут «блокировать» цикл событий. – kliron

+1

Вам придется переместить процесс, который занимает 30 минут, чтобы его собственный дочерний процесс, или закодировать его таким образом, чтобы он не блокировал цикл событий. –

ответ

1

Если ваша задача IO переплете идти с process.nextTick. Если ваша задача связана с процессором, асинхронные вызовы не будут предлагать много производительности. В этом случае вам нужно делегировать задачу другому процессу. Примером может быть порождение дочернего процесса, выполнение работы и возврат результатов в родительский процесс по завершении.

См. Nodejs.org/api/child_process.html для получения дополнительной информации.

Если ваше приложение должно это делать часто, то разворачивание множества дочерних процессов быстро становится ресурсом hog - каждый раз, когда вы используете fork, новый процесс V8 будет загружен в память. В этом случае, вероятно, лучше использовать один из мультипроцессорных модулей, например собственный Node Cluster. Этот модуль обеспечивает легкое создание и взаимодействие между процессами мастера-работника и может удалить большую сложность из вашего кода.

Смотрите также связанный с этим вопрос: Node.js - Sending a big object to child_process is slow

1

Главный поток Javascript в node.js однопоточен , Итак, если вы сделаете гигантскую петлю, связанную с процессором, то это приведет к зависанию одного потока, и никакая другая JS не будет запущена в node.js до тех пор, пока не будет выполнена одна операция.

Так что, когда вы звоните:

someModule.doSomething() 

и это все синхронные, то она не возвращается, пока это не будет сделано выполнения и, таким образом, строки кода ниже, что не не выполнить до doSomething() метод возвращает , И, как вы понимаете, использование обещаний с синхронным кодом CPU-hogging не помогает вашему делу вообще. Если это синхронно и связано с процессором, это займет много времени, прежде чем что-нибудь еще сможет запустить.

Если в цикле задействован ввод-вывод (например, ввод-вывод или сетевой ввод-вывод на диске), тогда есть возможность использовать операции асинхронного ввода-вывода и сделать код неблокирующим. Но, если нет, и это всего лишь много компонентов процессора, тогда он будет блокироваться, пока не будет выполнен, и никакой другой код не будет запущен.

Ваши возможности для изменения этого:

  1. Запуск процессора потребляющих кода в другом процессе. Либо создайте отдельную программу, которую вы запускаете в качестве дочернего процесса, через которую вы можете передавать входные данные, так и получать выходные данные или создавать отдельный сервер, на котором вы можете выполнить асинхронные запросы.

  2. Разбивайте неблокирующую работу на куски, где вы выполняете заготовки на 100 мс одновременно, затем возвращаете процессор обратно в цикл событий (используя что-то вроде setTimeout(), чтобы разрешить другие вещи в очереди на события и перед тем, как подобрать и запустить следующий фрагмент работы. Вы можете увидеть Best way to iterate over an array without blocking the UI за идеи о том, как выполнять синхронную работу.

В качестве примера можно было бы вырезать текущий цикл. Это длится до 100 мс циклов, а затем прерывает выполнение, чтобы дать другим возможность запустить. Вы можете установить время цикла на все, что хотите.

function SomeModule(){ 
    this.doSomething = function(){ 
     return q.Promise(function(resolve, reject, notify){ 
      var cntr = 0, numIterations = 10000, timePerSlice = 100; 
      function run() { 
       if (cntr < numIterations) { 
        var start = Date.now(); 
        while (Date.now() - start < timePerSlice && cntr < numIterations) { 
         notify('Progress '+cntr); 
         ++cntr; 
        } 
        // give some other things a chance to run and then call us again 
        // setImmediate() is also an option here, but setTimeout() gives all 
        // other operations a chance to run alongside this operation 
        setTimeout(run, 10); 
       } else { 
        resolve(); 
       } 
      } 
      run(); 
     }); 
    } 
} 
+0

Это напрямую связано с вашей записью и имеет визуальные эффекты на случай, если кто-то заботится. Перекрестная почта из/r/node https://www.youtube.com/watch?v=8aGhZQkoFbQ "Филипп Робертс: Что, черт возьми, цикл событий в любом случае? JSConf EU 2014" – Ravenous

Смежные вопросы