Правильная обработка жизненного цикла HTTP-соединения при длительных запросах

#javascript #node.js #express #sockets #asynchronous

Вопрос:

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

Тяжелая задача ввода-вывода может занять до 30 минут, поэтому мне интересно, в каком состоянии после этого остается соединение между сервером и клиентом res.json(...) ? Соединение с сокетом все еще открыто / полуоткрыто? Должен ли я в этом случае удалить сокет вручную после res.json(...) этого ? Нормально ли, что heavyIO задача завершается неудачно через 29 минут и переходит к экспресс-обработчику ошибок? И, наконец, является ли этот шаблон неверным, например, должен ли я обрабатывать задачи ввода-вывода совершенно по-другому?

Ниже приведен минимальный пример:

 import express from 'express'

const app = express()

const asyncHandler = (handler) =>
  (req, res, next) => Promise.resolve(handler(req, res, next)).catch(next)
const heavyIO = () =>
  new Promise((resolve, reject) => setTimeout(() => Math.random() > 0.5 ? resolve() : reject('FAILED'), 1000))

app.get('/start', asyncHandler(async (_req, res) => {
  // I want to be able to initiate the process and send back
  // confirmation that the process has been started. I want the
  // client to be able forget about this request.
  res.json('ok...started process')

  // Then we start to run the heavy IO process. Without the "await" this could
  // lead to unhandled promise rejection.

  // If this fails, the rejected promise will flow to express default
  // error handling which is I think "finalhandler" by default.
  // Because the headers as sent (res.headersSent === true) the error
  // handler will log the error and destroy the socket connection.

  // If this succeeds, the server will probably close the socket connection as well
  // internally.

  // Up until this point the socket has been open (or half-open?) so is this a problem?
  
  // Does it mean that the client has been "up-keeping" this socket connection
  // somehow or has/can it have been timed out before this succeeds or fails.
  await heavyIO()
  console.log(`FINISHED I/O at ${new Date().toISOString()}! Exiting handler...`)
}))

app.listen(8080)
 

Комментарии:

1. «было ли/может ли истечь время ожидания до того, как это произойдет успешно или не удастся». Клиент может прервать соединение. Но я не понимаю, зачем вам нужна связь после res.json('ok...started process') этого . Он может быть закрыт после этой строки.

2. @jabaa это правда. Если ввод-вывод завершится успешно или завершится неудачно, мы, по крайней мере, не должны уведомлять клиента об обратном использовании этого соединения. Как мне закрыть соединение?

3. Я не понимаю, чего вы здесь пытаетесь достичь. Почему ты вообще держишь связь открытой? Почему вы используете asyncHandler и async ? Зачем вам await эта функция heavyIO ? После этого связь между клиентом и сервером отсутствует res.json('ok...started process') .

4. Если вы закроете соединение, клиенту потребуется регулярно запрашивать статус. Для процесса, который занимает значительное время, поддержание соединения открытым, ничего не делая в течение 30 минут или более, кажется расточительным (для меня). Объединение через регулярные промежутки времени кажется более адекватным. Альтернативой может быть использование веб-сокетов для обеспечения всегда открытого канала связи между клиентом и сервером, который позволяет серверу отправлять обновления о ходе выполнения. Учитывая, что веб-сокеты более сложны и, следовательно, могут создавать свои собственные проблемы, я бы пошел с опросом.

5. @Tomalak Где вы видите общение после res.json('ok...started process') ? Статус и заголовок будут отправлены. Будет отправлено ответное сообщение. Что вы хотите опросить?