首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >内存不足的节点脚本(循环中的循环)

内存不足的节点脚本(循环中的循环)
EN

Stack Overflow用户
提问于 2017-07-26 21:06:29
回答 2查看 668关注 0票数 1

我有一个非常基本的脚本,我一直在添加的功能一点一点-几乎没有优化。

它应该通过X brands并为每个调用运行一个API调用(每个调用都有一个唯一的端点),然后将结果保存到CSV。

CSV部分工作得很好,虽然有点慢。(200 20文件约20秒)。

但是,当我将所有这些都封装在brands的一个循环中时,现在就超时了。默认情况下,它将运行2分钟,然后给我一个错误FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

我学会了附加--max_old_space_size=4000000来给它额外的内存(它也死了2GB)。在4GB时,我没有收到错误,但是我收到了Windows内存泄漏警告,所以很明显,我的脚本有问题。我觉得它在循环中循环,但不确定。

代码语言:javascript
复制
var http = require("https");
var qs = require("querystring");
var csvWriter = require('csv-write-stream');
var fs = require('fs');
var moment = require('moment');
let key = '';
var brands = ['REDACTED1','REDACTED2','REDACTED3','REDACTED4','REDACTED5','REDACTED6','REDACTED7','REDACTED8']
var dataRequest = function(token){
  var date = new Date()-1; //Make it yesterday
  var formattedDate = moment(date).format('YYYYMMDD');
  var yesterdayDashes = moment(date).format('YYYY-MM-DD');
  for (var k=0;k<=brands.length;k++){

    var headers = [];
    var csvBody = [];
    var csvContent = "data:text/csv;charset=utf-8,";
    var options = {
      "method": "GET",
      "hostname": "REDACTED",
      "port": "443",
      "path": "REDACTED/"+brands[k]+"/"+yesterdayDashes+"?REDACTED&access_token="+token,
      "headers": {
        "authentication": "Bearer "+token,
        "content-type": "application/json",
        "cache-control": "no-cache"
      }
    }

    var req = http.request(options, function (res) {
      var chunks = [];

      res.on("data", function (chunk) {
        chunks.push(chunk);
      });

      res.on("end", function () {
        var body = Buffer.concat(chunks);
      var object = JSON.parse(body);
      var writer = csvWriter({
        headers: ["REDACTED1", "REDACTED2", "REDACTED3", "REDACTED4", "REDACTED5", "REDACTED6", "REDACTED7", "REDACTED8", "REDACTED9"]
      })
      writer.pipe(fs.createWriteStream(brands[k] +'_' +formattedDate +'_DEMOGRAPHIC.csv'))
      for (var i=0;i<object.length; i++){
        writer.write([
          object[i].field1.REDACTED1, 
          moment(object[i].optin.REDACTED2).format('YYYYMMDD HHMMSS'), 
          object[i].field2.REDACTED1, 
          object[i].field2.REDACTED2, 
          object[i].field2.REDACTED3, 
          object[i].field2.REDACTED4, 
          object[i].field2.REDACTED5, 
          object[i].field3.REDACTED6, 
          object[i].field3.REDACTED7
        ])
      }
      writer.end()
      });
    });
    req.end();
  }
}

var authToken = function(){

  var form = qs.stringify({
    grant_type: 'client_credentials',
    client_credentials: 'client_id:client_secret',
    client_id: 'cdg-trusted-client',
    client_secret: 'REDACTED'
  })
  var options = {
    "method": "POST",
    "hostname": "REDACTED3",
    "port": null,
    "path": "REDACTED3",
    "headers": {
      "Content-Type": "application/x-www-form-urlencoded",
      "cache-control": "no-cache"
    }
  };

  var req = http.request(options, function (res) {
    var chunks = [];

    res.on("data", function (chunk) {
      chunks.push(chunk);
    });

    res.on("end", function () {
      var body = Buffer.concat(chunks);
      var json = JSON.parse(body);
      key = json['access_token'];
    });
  });

  req.write(form);
  req.end();
  dataRequest(key);
}
authToken();

我删除了敏感信息,但所有的逻辑都保留了下来。这是一个我很快就拼凑在一起的脚本,但老实说,我并没有看到它需要这么多内存的任何理由。我想这可能是无限循环,但是直接测试节点内的每个循环,我没有问题。

流开始获取承载令牌一次,然后将其传递给函数以获取数据。

当我讨论将其放入CodeReview中时,这段代码在技术上根本不起作用。

修改第一个循环的Update现在会立即输出unidentified CSV,而不会完全输出任何其他内容。但是,console.log(brands[k])正在输出适当的文件。

更新2

我的JS调试版本使console.log()无处不在,我注意到当我进入http.request init之后,brandsk突然变得没有定义。我想这可能是因为它没有传递到函数中而引起的?

更新3

我的undefined问题是由缺少分号引起的,它提前结束了for循环。我已经纠正了它,但现在我有一个最大的堆栈跟踪问题再次。

我的问题上下文现在似乎是“如何使这个for不运行异步?”

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-07-26 21:22:41

这是您的问题:for (var k=0;k=brands.length;k++){,您使用了操作符=,它是赋值操作符,并将品牌的长度放入变量k中,而不是检查k是否仍然更小的<操作符,然后是brands.length

所发生的情况是,在循环中测试的表达式是数字8(品牌数组的长度),它总是保持为8,所以您将进入一个无限循环。

注意,使用布尔表达式,与0不同的任何值都表示值true,而0表示false。

这是关于无限循环问题,还请注意,在将令牌传递给dataRequest函数时,代码还有另一个问题。

问题在于您试图在发送请求的req.end()之后立即传递它。

此时,响应仍然不存在,res.on("end", ...的回调函数仍未执行。基本上,您是将未定义的信息传递给dataRequest函数,这将导致您的第二个错误。只需将对dataRequest的调用移动到end回调中,如下所示:

代码语言:javascript
复制
res.on("end", function () {
   var body = Buffer.concat(chunks);
   var json = JSON.parse(body);
   key = json['access_token'];
   dataRequest(key);
});
票数 4
EN

Stack Overflow用户

发布于 2017-07-26 23:15:23

代码语言:javascript
复制
var http = require("https");
var qs = require("querystring");
var csvWriter = require('csv-write-stream');
var fs = require('fs');
var moment = require('moment');
let key = '';
var brands = ['REDACTED1','REDACTED2','REDACTED3','REDACTED4','REDACTED5','REDACTED6','REDACTED7','REDACTED8']
var dataRequest = function(token, client){
  var date = new Date()-1; //Make it yesterday
  var formattedDate = moment(date).format('YYYYMMDD');
  var yesterdayDashes = moment(date).format('YYYY-MM-DD');
    var headers = [];
    var csvBody = [];
    var csvContent = "data:text/csv;charset=utf-8,";
    var options = {
      "method": "GET",
      "hostname": "REDACTED",
      "port": "443",
      "path": "REDACTED/"+client+"/"+yesterdayDashes+"?REDACTED&access_token="+token,
      "headers": {
        "authentication": "Bearer "+token,
        "content-type": "application/json",
        "cache-control": "no-cache"
      }
    }; // <--- This was the culprit

    var req = http.request(options, function (res) {
      var chunks = [];

      res.on("data", function (chunk) {
        chunks.push(chunk);
      });

      res.on("end", function () {
        var body = Buffer.concat(chunks);
      var object = JSON.parse(body);
      var writer = csvWriter({
        headers: ["REDACTED1", "REDACTED2", "REDACTED3", "REDACTED4", "REDACTED5", "REDACTED6", "REDACTED7", "REDACTED8", "REDACTED9"]
      })
      writer.pipe(fs.createWriteStream(client +'_' +formattedDate +'_DEMOGRAPHIC.csv'))
      for (var i=0;i<object.length; i++){
        writer.write([
          object[i].field1.REDACTED1, 
          moment(object[i].optin.REDACTED2).format('YYYYMMDD HHMMSS'), 
          object[i].field2.REDACTED1, 
          object[i].field2.REDACTED2, 
          object[i].field2.REDACTED3, 
          object[i].field2.REDACTED4, 
          object[i].field2.REDACTED5, 
          object[i].field3.REDACTED6, 
          object[i].field3.REDACTED7
        ])
      }
      writer.end()
      });
    });
    req.end();
  }
}

var authToken = function(){

  var form = qs.stringify({
    grant_type: 'client_credentials',
    client_credentials: 'client_id:client_secret',
    client_id: 'cdg-trusted-client',
    client_secret: 'REDACTED'
  })
  var options = {
    "method": "POST",
    "hostname": "REDACTED3",
    "port": null,
    "path": "REDACTED3",
    "headers": {
      "Content-Type": "application/x-www-form-urlencoded",
      "cache-control": "no-cache"
    }
  };

  var req = http.request(options, function (res) {
    var chunks = [];

    res.on("data", function (chunk) {
      chunks.push(chunk);
    });

    res.on("end", function () {
      var body = Buffer.concat(chunks);
      var json = JSON.parse(body);
      key = json['access_token'];
      for (var k=0;k<=brands.length;k++){
         var client = brands[k];
         dataRequest(key, client);

    });
  });

  req.write(form);
  req.end();

}
authToken();

最后,正如上面所述,我在使用k = x时遇到了问题,一旦我将它修改为k < x,我已经对代码进行了多次更改,导致了上面的更新。除了k = x,我在上面的位置上有一个分号,这破坏了我的for

我还选择了每个brand启动一个呼叫,而不是在品牌内发起它。这个脚本现在运行得很好。

我增加了一个次要的答案,以避免夸大问题,并在最后显示整个工作代码。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45337444

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档