首页 > 解决方案 > 在接收 POST 数据之前,如何防止 Heroku 上部署的 NodeJS Web 应用程序退出?

问题描述

我在基于服务器的应用程序方面非常缺乏经验(并且在客户端应用程序方面经验略多)。但是,基于一些示例(主要是在 Stack Overflow 上),我创建了一个基本的 NodeJS 网络应用程序,它在本地运行时可以正常工作(例如“heroku 本地网络”)。但是在部署时,应用程序不会收到 POSTed 数据,除非我插入延迟。我确定我做错了什么,所以我希望有人能指出我处理这个问题的正确方法。我已经将应用程序提炼为基本问题,仍然使用下面的代码显示。

基本上,据我了解,简单的网页将 JSON 数据发布到我的网络应用程序。Web 应用程序确实收到了 POST 消息。但在网络应用程序可以收集正在传输的数据之前,网络应用程序似乎退出了。当应用程序在本地运行时数据不会丢失(当我在 Google Cloud 上部署早期版本时数据也不会丢失)。但是,当部署在 Heroku(免费层)上时,确实会发生损失。

其他几种语言的经验让我对为什么 NodeJS 应用程序在收到任何消息之前并不总是退出感到困惑,因为应用程序中没有代码来延迟退出(例如消息泵或等待退出信号)。当它奏效时,我只是耸了耸肩,并决定有一些魔法在起作用,我稍后会了解。但实际上,我所看到的失败对我来说更有意义。

无论如何,正是这种怀疑导致我添加了延迟(添加如下所示的行以及//***的注释)。由此我能够确定数据正在到达,但没有被收集,没有延迟。

以下是主要代码文件。下面是一些显示延迟添加前后本地和远程行为的日志。

任何帮助表示赞赏。

索引.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang='en'>
<meta http-equiv='Content-Language' content='en'>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>

<script>

var scriptlocation = location.href.replace(document.location.pathname, '');
var request = new XMLHttpRequest();

function OnReadyStateChange() {
  if (request.readyState == 4 && request.status == 200) {
    console.log("Data upload complete.");
  }
}

function SendDataToServer(data) {
  var url = scriptlocation + "/server.js";
  request.open('POST', url, true);
  request.setRequestHeader('Content-type', 'application/json');
  request.onreadystatechange = OnReadyStateChange;
  request.send(data);
}

function SendEntryDataToServer() {
  var entryData = {
    fruit: "apple",
    size: "medium",
    color: "red"
  };
  console.log("*** SENDING DATA TO SERVER ***");
  var postData = JSON.stringify(entryData);
  SendDataToServer(postData);
  return false;
}

</script>
</head>
<body>
  <form>
    <input type="button" onclick="SendEntryDataToServer()" value="Post Data"/>
  </form>
</body>

server.js:

const http = require('http');
const fs = require('fs');

var server = http.createServer(
  function (req, res) {
    console.log(`req.url: ${req.url}\n`);

    if (req.method == 'POST') {
      console.log("POST");

      var body = '';

      req.on('data', function (data) {
        body += data;
      });

      req.on('end', function (data) {
        console.log(JSON.parse(body));
      });

      res.writeHead(200, {'Content-Type': 'text/html'});
      res.end('post received');

      console.log("Sleeping 5 seconds"); // ***
      setTimeout(function(){             // ***
        console.log("POST end");
      }, 5000);                          // ***
    } else {
      console.log("GET");

      fs.readFile('./' + req.url, function(err, data) {
        if (!err) {
          var dotoffset = req.url.lastIndexOf('.');
          var mimetype = dotoffset == -1
                        ? 'text/plain'
                        : {
                          '.html' : 'text/html',
                          '.ico' : 'image/x-icon',
                          '.jpg' : 'image/jpeg',
                          '.png' : 'image/png',
                          '.gif' : 'image/gif',
                          '.css' : 'text/css',
                          '.js' : 'text/javascript'
                        }[ req.url.substr(dotoffset) ];
          res.setHeader('Content-type' , mimetype);
          res.end(data);
        } else {
          console.log ('file not found: ' + req.url);
          res.writeHead(404, "Not Found");
          res.end();
        }
      });
    }
  }
);

const PORT = process.env.PORT || 5000;
server.listen(PORT, () => {
  console.log(`Server is running (port: ${PORT})...`);
});

console.log("FILE end");

延迟添加之前的本地:

10:24:00 PM web.1 |  FILE end
10:24:00 PM web.1 |  Server is running (port: 5000)...
10:24:14 PM web.1 |  req.url: /index.html
10:24:14 PM web.1 |  GET
10:24:18 PM web.1 |  req.url: /server.js
10:24:18 PM web.1 |  POST
10:24:18 PM web.1 |  POST end
10:24:18 PM web.1 |  { fruit: 'apple', size: 'medium', color: 'red' }

在添加延迟之前部署:

2019-12-18T04:31:52.835329+00:00 app[web.1]: FILE end
2019-12-18T04:31:52.837439+00:00 app[web.1]: Server is running (port: 17378)...
2019-12-18T04:32:14.929250+00:00 heroku[router]: at=info method=GET path="/index.html" host=****.herokuapp.com request_id=**** fwd="*.*.*.*" dyno=web.1 connect=0ms service=9ms status=200 bytes=1233 protocol=https
2019-12-18T04:32:14.925381+00:00 app[web.1]: req.url: /index.html
2019-12-18T04:32:14.925407+00:00 app[web.1]:
2019-12-18T04:32:14.925509+00:00 app[web.1]: GET
2019-12-18T04:32:25.004774+00:00 app[web.1]: req.url: /server.js
2019-12-18T04:32:25.004809+00:00 app[web.1]:
2019-12-18T04:32:25.004964+00:00 app[web.1]: POST
2019-12-18T04:32:25.006023+00:00 app[web.1]: POST end

本地延迟后添加:

10:48:04 PM web.1 |  FILE end
10:48:04 PM web.1 |  Server is running (port: 5000)...
10:48:08 PM web.1 |  req.url: /index.html
10:48:08 PM web.1 |  GET
10:48:12 PM web.1 |  req.url: /server.js
10:48:12 PM web.1 |  POST
10:48:12 PM web.1 |  Sleeping 5 seconds
10:48:12 PM web.1 |  { fruit: 'apple', size: 'medium', color: 'red' }
10:48:17 PM web.1 |  POST end

添加延迟后部署:

2019-12-18T04:51:50.925802+00:00 app[web.1]: req.url: /index.html
2019-12-18T04:51:50.925831+00:00 app[web.1]:
2019-12-18T04:51:50.925944+00:00 app[web.1]: GET
2019-12-18T04:51:56.071684+00:00 heroku[router]: at=info method=POST path="/server.js" host=****.herokuapp.com request_id=**** fwd="*.*.*.*" dyno=web.1 connect=0ms service=6ms status=200 bytes=151 protocol=https
2019-12-18T04:51:56.064644+00:00 app[web.1]: req.url: /server.js
2019-12-18T04:51:56.064659+00:00 app[web.1]:
2019-12-18T04:51:56.068033+00:00 app[web.1]: POST
2019-12-18T04:51:56.069013+00:00 app[web.1]: Sleeping 5 seconds
2019-12-18T04:51:56.075197+00:00 app[web.1]: { fruit: 'apple', size: 'medium', color: 'red' }
2019-12-18T04:52:01.073243+00:00 app[web.1]: POST end

标签: node.jshttppostherokuweb-applications

解决方案


万一以后有人遇到这个问题,我没有得到任何反馈,也找不到合理的解决方法。(我尝试了 Promise 和其他一些临时信号。)所以我通过使用 Express 作为我的服务器来回避这个问题,因为我知道这已在全球范围内成功部署。考虑到极其简单的数据传输,切换是微不足道的,而且效果很好。(我打算深入研究 Express 源代码,看看他们是如何处理它的,但似乎将它包含在我的 NodeJS 应用程序中会导入超过 200 个其他包,所以这非常令人沮丧。)


推荐阅读