Упрощенный подход

После запроса на объединение изменений и частного обсуждения с автором я понял, что некоторые мои знания о Express / Node, Kubernetes и о том, как они работают вместе, не разделяются всеми на сайте. конкретный предмет. Это означает, что другие не знают, почему мы делаем что-то определенным образом, и эта статья пытается решить эту проблему, поэтому эти знания передаются не только внутри компании, но и всем.

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

router.get('/my-route',
  asyncHandler(async (req, res) => {
    await myPromise(req.body)
    return res.reply(OK, 'SUCCESS')
  }))

Чтобы понять, почему это важно, важно понимать, как работают Node и Express, и что происходит под капотом.

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

Express, который выполняется Node, слушает входящий порт, обрабатывает HTTP-запросы и вызывает набор связанных обратных вызовов, соответствующих маршруту. Эти связанные обратные вызовы включают в себя такие вещи, как наша проверка, промежуточное программное обеспечение и, в конечном итоге, основной метод, который мы хотим выполнить для этого маршрута. Express (используемый нами) не обрабатывает обещания или асинхронные методы, он использует метод обратного вызова, чтобы знать, когда перейти к следующему обратному вызову в цепочке. Вы видели много фрагментов кода, которые вызывают next() или res.reply(), последний из которых является методом easyfundraising, привязанным к объекту res для обработки выдачи ответов. Это означает, что нет встроенного улова верхнего уровня для любой выдаваемой ошибки или отклоненного обещания.

Из-за асинхронного характера Node это означает, что простой вызов функции обратного вызова (которая next() и reply()are) не остановит автоматически выполнение базового кода. Мы знаем, что если мы не ожидаем чего-то асинхронного, наше выполнение все равно продолжится. Это означает, что при написании хорошего кода мы всегда return next() или return reply() в маршрутах, чтобы принудительно остановить выполнение или даже просто показать намерение, если это последняя строка. Однако есть случаи, когда мы хотим в полной мере воспользоваться этой асинхронной природой и внутренне разрешить продолжение выполнения после выдачи ответа. Цель этого - предотвратить ожидание чего-либо внешнего без уважительной причины для завершения фоновой задачи. Подход, который вы увидите здесь, заключается в том, чтобы вызвать reply() без возврата, а затем перейти к другим действиям. Но по-прежнему так же важно, чтобы мы отлавливали любые ошибки или отклонения, потому что только потому, что клиент не слушает, ошибки все равно могут привести к тому, что этот единственный поток в Node умирает.

Как мы знаем из всех языков, любая возникшая и не обнаруженная ошибка остановит выполнение текущего процесса и приведет к его завершению. Помните, что под капотом Node находится однопоточный процесс, что означает, что если процесс умирает, Node останавливается. Это важно для модулей, потому что это означает, что ничто не слушает входящие порты и не обрабатывает запросы. Зонды жизнеспособности Kubernetes в конечном итоге обнаруживают это и перезапускают модуль, но краткий ответ заключается в том, что неперехваченная ошибка или отклонение обещания приведет к тому, что модуль, на котором запущено экспресс-приложение, умрет и потребует перезапуска.

Так что все это значит для нас? Это означает, что крайне важно убедиться, что все маршруты успешно и правильно перехватывают все ошибки, которые могут быть вызваны вызываемыми ими методами / функциями / лямбда-выражениями. Если они этого не сделают, то сбой приведет к тому, что дежурные инженеры будут разбужены в любое время суток, и все мы знаем, насколько сварливыми и бессонными могут быть дежурные инженеры.

Вот секрет, как сделать это легко. Умный разработчик внес asyncHandler() в приложения, которые будут использоваться маршрутами.

/**
 * Wrap the given function in a try catch block to ensure all errors are successfully caught.
 * @param {Function} fn
 * @return {Function}
*/
const asyncHandler = (fn) => {
  return async (req, res, next) => {
    try {
      return await fn(req, res, next)
    } catch (err) {
      next(err)
    }
  }
}
module.exports = {
  asyncHandler,
}

Обработчик async - это не более чем прославленный блок try-catch, который обрабатывает любую обнаруженную ошибку обратно в среду Express. Его сила в том, что, заключив наши маршруты в обработчик, нам не нужно писать сотни блоков try-catch или методов .catch (), чтобы знать, что код безопасен.

TL;DR

Из-за того, как Node, Express и Kubernetes работают вместе, вы ДОЛЖНЫ выявлять все ошибки и обещать отказ, несмотря ни на что. Невыполнение этого требования приведет к тому, что дежурные инженеры будут сварливыми и бессонными.