2016-08-17 135 views
1

我試圖通過JSONStream.parse管道輸入流(從巨大的GeoJSON文件創建) ()將流分解成對象,然後通過event-stream.map()讓我轉換對象,然後通過JSONStream.stringify()創建一個字符串,最後創建一個可寫的輸出流。隨着進程的運行,我可以看到節點的內存佔用量繼續增長,直到最終耗盡堆。下面是重現該問題的最簡單的腳本(test.js):即噴出JSON源源不斷地爲節點的process.stdin會造成節點的堆逐步成長當通過es.map()和JSONStream.stringify()管道JSONStream.parsed()將文件流管道輸入流

const fs = require("fs") 
const es = require("event-stream") 
const js = require("JSONStream") 

out = fs.createWriteStream("/dev/null") 
process.stdin 
    .pipe(js.parse("features.*")) 
    .pipe(es.map(function(data, cb) { 
     cb(null, data); 
     return; 
    })) 
    .pipe(js.stringify("{\n\"type\": \"FeatureCollection\", \"features\": [\n\t", ",\n\t", "\n]\n}")) 
    .pipe(out) 

小bash腳本(barf.sh):

#!/bin/bash 

echo '{"type":"FeatureCollection","features":[' 
while : 
do 
    echo '{"type":"Feature","properties":{"name":"A Street"}, "geometry":{"type":"LineString"} },' 
done 

運行它像這樣:

barf.sh | node test.js 

有幾個好奇的方式來回避這個問題:

  • 取出fs.createWriteStream()和 「.pipe(出)」 改變最後一個管道階段 「.pipe(process.stdout)」,然後管節點的標準輸出到/ dev/null的
  • 更改異步es.map()同步es.mapSync()

前兩個動作之一將允許腳本永遠運行,節點的內存佔用低且不變。我在運行Ubuntu 16.04的8GB內存的八核心機器上使用節點v6.3.1,事件流v3.3.4和JSONStream 1.1.4。

我希望有人能幫助我糾正我所確定的一個明顯的錯誤。

回答

2

JSONStream不是streams2流,所以它不支持背壓。 (有一個簡要約streams2here。)

這意味着數據是走出來的parse流的data事件和數據流將會保持抽水出來,無論是否消費流準備好了。如果在管道之間的某處可以讀取和寫入的速度有所不同,那麼會有緩衝 - 這就是你所看到的。

您的barf.sh線束看到通過stdin泵送的功能。相反,如果您正在讀取大量文件,您應該可以通過暫停文件的讀取流來管理流程。所以如果你想在你的map回調中插入一些pause/resume邏輯,你應該能夠得到它來處理一個巨大的文件;它會花更長的時間。我會用這樣的實驗:

let in = fs.createReadStream("/some/massive/file"); 
let out = fs.createWriteStream("/dev/null"); 
in 
    .pipe(js.parse("features.*")) 
    .pipe(es.map(function(data, cb) { 
     // This is just an example; a 10-millisecond wait per feature would be very slow. 
     if (!in.isPaused()) { 
      in.pause(); 
      global.setTimeout(function() { in.resume(); }, 10); 
     } 
     cb(null, data); 
     return; 
    })) 
    .pipe(js.stringify("{\n\"type\": \"FeatureCollection\", \"features\": [\n\t", ",\n\t", "\n]\n}")) 
    .pipe(out); 

順便說一句,使用mapSync使得小到沒有我的電腦上的區別(這是又老又慢)。但是,除非您在map中執行一些異步操作,否則我會使用mapSync

+0

謝謝。我直言不諱地說,爲什麼將目標流更改爲process.stdout允許代碼無限期地運行而不增加內存。你能重現那種行爲嗎? – nw31304

+0

對我來說,它似乎仍然管道傳輸到'stdout'時消耗的內存,但在* *得多率較低。我猜'fs'基礎結構必須比'stdout'慢 - 即使是'/ dev/null'。這是所有推送流的問題;您可以使用RxJS進入類似的情況。我認爲你最好的選擇是做一些試驗來暫停閱讀。也許每隔1000個功能就會暫停一下? – cartant