Асинхронный setTimeout блокирует экспресс-обработку, пока не произойдет res.send - PullRequest
0 голосов
/ 04 апреля 2019

У меня есть асинхронная операция, которая делегируется рабочему процессу.

Поскольку каждая операция занимает примерно 2 секунды, я хотел передать результат этой операции в ответ на запрос.

Я завернул выполнение рабочего процесса в обещание, которое не разрешается до тех пор, пока рабочий процесс не завершит работу с интенсивным использованием ЦП и вернет результат:

module.exports = (req, res, controller) => {
  res.setHeader('Content-Type', 'application/json');

  const workerService = controller.services.workers;
  if (!workerService) {
    return res.json({
      ok: false,
      info: 'Worker service has not been started. Relaunch API server and check logs.'
    });
  }

  let commandObject = '';
  try {
    const postBody = req.body;
    // build a backtest execution command based on request params
    commandObject = getWorkerCommandFromPostBody(postBody);

  } catch (exception) {
    return res.json({
      ok: false,
      info: exception
    });
  }

  return controller.services.workers
    .promiseWorkerExecution(commandObject)
    .then(result => {
      if (result.context && result.context.results) {
        return res.end(result.context.results);
      }

      return res.end(result);
    })
    .catch(e => res.end(e));
}

Здесь я запутался. Основной поток, в котором выполняется мой экспресс-процесс, не блокируется. Вся интенсивная операция делегируется выделенному подпроцессу (через модуль «кластер»).

Однако, если я инициирую 3 из этих запросов, 2-й запрос даже не начнет обрабатываться, пока первый запрос не увидит res.end(). Это просто ожидание обещания решить. Я уверен, что это может быть воспроизведено с обещанием, ожидающим, пока setTimeout разрешится через несколько секунд.

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

Нет ли способа продолжить прием параллельных запросов на экспресс-сервер, не выполняя также кластер на экспресс-сервере? Я надеялся, что один экспресс-сервер и отдельные (ограниченные) работники будут делегировать / обрабатывать нагрузку на процессор.

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

Редактировать: я должен добавить, что я знаю об однопоточной природе Node, но я думал, что факт обещания разрешает параллельные асинхронные операции, пока они не блокируют поток ...

Редактировать: я воспроизвел проблему в песочнице, используя только setTimeout для задержки res.send:

Привет всем, так что его можно воспроизвести с помощью setTimeout, который явно не требует интенсивной блокировки процессора. Песочница: https://codesandbox.io/embed/nr41lkw95j

Я тестирую 3 параллельных запроса, вызывая это в терминале: curl https://nr41lkw95j.sse.codesandbox.io/test && curl https://nr41lkw95j.sse.codesandbox.io/test && curl https://nr41lkw95j.sse.codesandbox.io/test

Терминал на сервере песочницы показывает, что он блокирует обработку запроса, пока текущий запрос не отправит ответ, даже если что-то происходит в асинхронном обратном вызове

1 Ответ

2 голосов
/ 05 апреля 2019

Проблема в том, как вы выполняете запросы, а не в том, как вы выполняете запросы параллельно.

curl https://nr41lkw95j.sse.codesandbox.io/test && curl https://nr41lkw95j.sse.codesandbox.io/test && curl https://nr41lkw95j.sse.codesandbox.io/test

Когда вы используете &&, он ожидает, пока последняя команда будет выполнена правильно, без появления ошибки для выполнения следующей. Если вы хотите выполнять запросы параллельно, вы должны сделать следующее:

curl https://nr41lkw95j.sse.codesandbox.io/test &
curl https://nr41lkw95j.sse.codesandbox.io/test &
curl https://nr41lkw95j.sse.codesandbox.io/test &

С одним &, поэтому он работает в фоновом режиме.

Альтернативой является использование apache benchmark или аналогичных программ.

ab -n 9 -c 3 https://nr41lkw95j.sse.codesandbox.io/test

Приведенная выше команда выполняет всего 9 запросов с параллелизмом 3. Запустив его, вы увидите:

received request:  10
received request:  11
received request:  12
sent response:  10
sent response:  11
sent response:  12
received request:  13
received request:  14
received request:  15
sent response:  13
sent response:  14
sent response:  15
received request:  16
received request:  17
received request:  18
sent response:  16
sent response:  17
sent response:  18

Это свидетельствует о том, что Node.js может обрабатывать несколько запросов одновременно, если вы не заблокируете цикл обработки событий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...