2017-07-26 127 views
0

我有,我一直在滴滴上添加功能位一個非常基本的腳本 - 這也很難優化。節點腳本(一個循環內循環)運行內存

它應該通過X brands併爲每個API運行一個API調用(每個調用都有一個唯一的端點),然後將結果保存爲CSV。

的CSV部完美地工作,儘管是一點點慢。 (對於200KB文件~20秒)。

然而,當我去包裝所有它的一個循環內的brands現在超時。默認情況下,它會運行2分鐘,然後給我一個錯誤 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

我學會追加--max_old_space_size=4000000給它多餘的內存(它也死在2GB)。在4GB時,我沒有收到錯誤,但是我收到了Windows內存泄漏警告,所以這非常明顯,我的腳本有問題。我覺得它在循環中循環,但不確定。

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,這個代碼不是在技術上工作。

更新 現在修改第一for循環立即輸出unidentified CSV並沒有完全別的。但console.log(brands[k])正在輸出適當的文件。

更新2

我的JS調試版本是把console.log()無處不在,我發現,一旦我得到下面的http.request初始化,品牌[K]突然變得不確定。我認爲這可能是因爲它沒有傳遞給函數?

更新3

undefined問題是由缺少分號引起的,早期的for循環結束。我已經糾正它,但現在我再次出現Max Stack Trace問題。

我的問題情境,似乎現在是「我如何使這個for無法運行異步?」

+0

我相信節點不應該是這種大文件的選擇。我會用C來執行這個操作,並用Node – Volem

回答

3

這裏是你的問題: for (var k=0;k=brands.length;k++){ 您已經使用了運營商=這是賦值運算符,把品牌的長度爲可變k代替<操作的是檢查是否k仍然較小然後brands.length

發生什麼事情是循環中測試的表達式是數字8(品牌數組的長度),它始終保持爲8,因此您將陷入無限循環。

請注意,在布爾表達式中,與0不同的值表示值爲true,而0表示爲false。

這是關於無限循環問題,還注意到您的代碼有另一個問題,將令牌傳遞給dataRequest函數。

問題是你試圖在req.end()後立即傳遞它。發送請求。

此時響應仍然不存在,並且res.on("end", ...的回調函數仍未執行。 基本上你將undefined傳遞給導致你的第二個錯誤的dataRequest函數。只需將呼叫轉移到dataRequestend回調內部發生像這樣:

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

執行它呢?對我來說似乎很好。爲了以防萬一,我甚至嘗試過'k <= brands.length'。 – DNorthrup

+0

感謝您的更新。我沒有想到這一點。我已經更新了我的問題 - 現在它立即輸出一個空的「未識別」文件,但是'console.log()'顯示品牌[k]正在被佔用。 – DNorthrup

+0

@DNorthrup你想'k <品牌。長度'不是'<='。 '品牌[brands.length]'將是未定義的。 Ori提到的問題會導致你的循環做出無限的請求。 –

0
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發起一個呼叫,而不是在品牌內發起呼叫。該腳本現在工作完美。

我已經添加了第二個答案,以不誇大問題,並在最後顯示整個工作代碼。