为什么不调用NodeJS express的POST处理程序,但RAM上升?

时间:2018-07-11 16:28:49

标签: node.js express request

我正在使用NodeJs 8.11.3

我有两个应用程序:第一个提供简单的Express'hello world API,以及其他一些库:

// API.js
const express = require('express')
const app = express()

var compression = require('compression')
var bodyParser = require('body-parser');

var helmet = require('helmet');
app.use(helmet());
app.use(compression());
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));



app.post('/', (req, res) => {
        res.send('Hello World!');
        console.log(`SENT in the FIRST PLACE at: ${req.body.event_datetime}, now is ` + new Date().toLocaleString());
});


app.listen(8000, () => console.log('Example app listening on port 8000!'))

,第二个是API的客户端。客户端发送一个发布请求,其中正文是 3.5MB的JSON对象

// client.js
const rp = require('request-promise');
function send_stuff(obj) {      
  let options = {
    uri: "http://xxxxx:8000/",
    method: "POST",
    headers: "headers": {
            "Content-Type": "application/json"
        },
    body: JSON.stringify(obj)
  }
  return rp(options);
}

var fs = require('fs');
const content= fs.readFileSync("./big_file.txt").toString();
(async () => {    
  for (let i = 0; i < 1000; ++i) {
    send_stuff(
      {
        id: 1,
        field: 1,
        field2: 1,
        big_content: content,
        id_domain: 1,
        event_datetime: new Date().toLocaleString(),
        field3: -1,
        field4: 1
      }).then(r => {           
        console.log('ok');           
      }).catch(err => {           
        console.error("API ERROR");
      });

      //await sleep(500);

  }
})();

function sleep(ms){
   return new Promise(resolve => setTimeout(resolve, ms) );
 }

有两件事我无法完全理解:

  1. 在客户端的for循环结束之前,我看不到API应用程序收到的任何数据包
  2. 监视API的RAM消耗,我注意到这会上升,直到应用程序开始处理请求为止。

问题:

关于 1),由于for循环使事件循环保持冻结状态,客户端是否可能无法发送数据包?

关于 2)的原因是API的RAM可能会继续上升,因为来自客户端的数据包实际上正在传入但非常缓慢,原因是它们试图发送3.5MB请求主体,因此在数据包完成之前,无法调用API中的POST处理程序?

1 个答案:

答案 0 :(得分:0)

“ 1。在客户端的for循环结束之前,我看不到API应用程序收到的任何数据包”

浏览器中的JavaScript解释器实现为单个线程。这实际上意味着在浏览器中一次只能发生一件事情,而其他动作或事件则被排在Execution Stack中。

for循环会立即运行到完成,同时启动所有异步操作。我看到您的代码中有await的注释部分。我不明白您为什么要对sleep使用await,因为它什么也没做。 await带有诺言,因此非常适合与进行API调用的函数配合使用。当您使用await时,您将serialize进行异步操作以逐个运行而不是并行运行。

在您的示例中,将是这样的:

(async () => {    
  for (let i = 0; i < 1000; ++i) {

    //THIS IS SERIALIZING send_stuff operation.

    await send_stuff(
      {
        id: 1,
        field: 1,
        field2: 1,
        big_content: content,
        id_domain: 1,
        event_datetime: new Date().toLocaleString(),
        field3: -1,
        field4: 1
      }).then(r => {           
        console.log('ok');           
      }).catch(err => {           
        console.error("API ERROR");
      });

  }
})();

这样做,您应该看到后端在for循环中每个POST收到increment个调用。

如果您不希望await,则for循环将在Execution Stack拿起asynchronous calls之前完成。这是因为for循环将形成current execution context。没错,current execution context的事件循环被冻结了。我建议阅读此书以了解更多信息-https://blog.lavrton.com/javascript-loops-how-to-handle-async-await-6252dd3c795

“ 2。监视API的RAM消耗,我注意到它会上升,直到应用程序开始处理请求为止。”

重要的是要了解XHR幕后要解决的问题。 POST在浏览器中实现为两个步骤。 (Mozilla是一个例外。他们一步一步地降低了TAT)。第一步,发送标头,然后发送数据。尽管这些操作应该最少,但它们都会消耗一些RAM。这要复杂得多,因为它涉及缓存,并且每个浏览器实现处理Cookie的方式有所不同。

除非网络服务器/服务器端代码施加了限制,否则传输的数据大小将变得无关紧要(假设您具有良好的Internet连接)。 content被序列化为String,因此它变成了一大堆文本。来回传输大量文本会影响异步分辨率。 GZIP压缩可能会对发送数据之前进行压缩以及在接收到响应之后解压缩数据所需的RAM量产生边际影响。

如果您选择压缩数据,则希望在请求发送到服务器之前看到RAM使用量增加,并且同样希望在收到响应后RAM使用量达到峰值。

希望这会有所帮助。