Почему иногда ошибка в асинхронном коде приводит к сбою сервера node.js - PullRequest
2 голосов
/ 10 апреля 2019

Я читал на некоторых сайтах, что в express.js «Любые необнаруженные ошибки в асинхронном коде могут привести к сбою HTTP-сервера, вызывающему DoS».Я сделал этот пример, чтобы проверить его, но я хочу знать, почему, если ошибка происходит внутри экспресс-обратного вызова, сервер не падает, но если это происходит внутри функции setTimeout (), сервер падает.

Не возникает ли ошибка в асинхронном коде в обоих примерах, или один из них не является асинхронным, и я ошибаюсь?Почему необработанная ошибка в некоторых асинхронных кодах приводит к сбою сервера, а в других асинхронных кодах нет?

var express = require("express");

var app = express();

http: app.get("/e1", (req, res, next) => {
  let p = req.query.p;
  let pn = parseInt(p, 10);

  //If the error happens here the server does not crashes
  let s = pn + y; // y does not exist, so an error occurs

  res.send("hi");
});

http: app.get("/e2", (req, res, next) => {
  let p = req.query.p;
  let pn = parseInt(p, 10);

  setTimeout(() => {
    //If the error happens here the server crashes
    let s = pn + y; // y does not exist, so an error occurs
  }, 100);

  res.send("hi");
});

app.listen(3000, function() {
  console.log("Example app listening on port 3000!");
});

1 Ответ

0 голосов
/ 10 апреля 2019

Это может стать понятным, если мы подумаем о throw и catch, работающих на стеке :

throw: идет вниз по стеку, пока не найдет обработчик, затемпродолжается оттуда.

catch добавляет обработчик ошибок в стек.

Для синхронного кода это можно представить как:

 // Legend:
 -> function call
 <- function returns

 http.request -> express.handler -> [try] -> your function -> nested call -> Throw!
 <-            <-                   [catch] <-----------------------------------

Теперь, когда вы запускаетеасинхронное действие, обратный вызов когда-нибудь будет вызван, и этот обратный вызов закончится в новом стеке:

 // the current stack:
 http.request -> express.handler -> [try] -> your function -> start async action
 <-            <-                 <-       <-             <-

 // somewhen later, the timeout calls back
 timer -> your setTimeout callback -> nested call -> Throw!
 Crash! <-----------------------------------------------------

Теперь, когда Express присоединяет обработчик catch к обратному вызову в ваш код:

 Express.get = function(callback) {
   //...
   try {
     callback(req, res, next);
   } catch(error) {
    // handle error
   }
 };

, который будет обрабатывать ошибки, но только синхронные (это упрощенно, фактический код здесь )


Теперь, что нужно сделать, чтобы справиться с этим?

В основном: Оберните каждый обратный вызов в обещание (поскольку это облегчает асинхронную обработку ошибок):

 const delay = ms => new Promise(res => setTimeout(res, ms));

затем await каждое созданное вами обещание и оберните все это в try / catch:

 app.get(async (req, res) => {
   try {
      await delay(2000);
      const p = q + d;
   } catch(error) {
     res.status(500).send("whoops");
  }
});

Это работает, потому что await "держит стек"(но только одна из async функций, которые await при вложенных вызовах, поэтому нам нужно добавить наш собственный try / catch, поскольку Express не await при вызове обратного вызова), поэтому ошибка внутриодна из вложенных await ed функций обратится к нашему обработчику ошибок.

 http.request -> express.handler -> [async] -> [try] -> nested call -> [await]
 <-                     <-               <-
 // synchronous code returned, the [async] stack will be kept
 [async] -> [try] -> nested call -> [await]

// somewhen, the promise resolves, execution coninues:
[async] -> [try] -> nested call -> Throw!
<-       <- [catch]  <--------------
...