2016-06-04 19 views
1

Im Moment versuche ich eine sehr große JSON-Objekt von einer API (besonders this eins), die abhängig von verschiedenen Faktoren kann mehr als ein paar MB sein. Das Problem ist jedoch, dass NodeJS für immer etwas braucht und dann nur noch nicht genug Speicher hat: Die erste Zeile meines Antwort-Callbacks wird nie ausgeführt.NodeJS http und extrem große Antwort Körper

Ich könnte jeden Artikel einzeln anfordern, aber das ist eine enorme Menge von Anfragen. Um den a-Entwickler hinter der neuen API zu zitieren:

Bis jetzt, wenn Sie alle Marktaufträge für Tranquility erhalten wollten, mussten Sie jeden Typ einzeln für jede Region anfordern. Das wären in der Regel 50+ Regionen multipliziert mit bis zu 13.000 Arten. Selbst wenn es nur 13.000 Typen und 50 Regionen waren, sind das 650.000 Anfragen, um alle Marktinformationen zu erhalten. Und wenn Sie alle Daten im 5-Minuten-Cache-Fenster abrufen wollten, würden Sie fast 2.200 Anfragen pro Sekunde benötigen.

Offensichtlich ist das keine gute Idee.

Ich versuche, das Array items für später in redis zu bekommen, dann folgen Sie der next URL und wiederholen, bis die letzte Seite erreicht ist. Gibt es eine Möglichkeit, dies zu tun?

EDIT: Hier ist der Problemcode. Der Besuch der URL funktioniert gut im Browser.

// ... 
    REGIONS.forEach((region) => { 
     LOG.info(' * Grabbing data for `' + region.name + '#' + region.id + '`'); 
     var href = url + region.id + '/orders/all/', next = href; 
     var page = 1; 
     while (!!next) { 
     https.get(next, (res) => { 
      LOG.info(' * * Page ' + page++ + ' responded with ' + res.statusCode); 
     // ... 

Die erste LOG.info Linie ausführt, während der zweite nicht der Fall ist.

+0

Wenn die Antwort nur ein paar MB ist, warum Sie nicht genügend Arbeitsspeicher ausgeführt werden? Ich denke, du würdest mit dieser Frage beginnen wollen. Ich habe gerade die JSON-Antwort gemessen und es ist 6,23 MB. – jfriend00

+0

Die Dokumentation warnt die Seite kann "mehrere" MB Größe sein, was alles bedeuten könnte. In beiden Fällen gibt es immer noch ein Problem mit dem Arbeitsspeicher und der Ausführungszeit. Es dauert nicht lange, den Link in einem Browser einfach zu besuchen. – NukesForKids

+0

Bitte zeigen Sie uns Ihren node.js Code. Es funktioniert sogar im Browser hier: https://jsfiddle.net/jfriend00/qscyqt7d/ – jfriend00

Antwort

4

Es scheint, dass Sie eine while(!!next) Schleife machen, die die Ursache Ihres Problems ist. Wenn Sie mehr vom Server-Code zeigen, könnten wir genauer beraten und sogar einen besseren Code vorschlagen.

Javascript führen Sie Ihren Code single threaded. Das bedeutet, dass ein Ausführungs-Thread vollständig ausgeführt wird, bevor andere Ereignisse ausgeführt werden können.

Also, wenn Sie tun:

while(!!next) { 
    https.get(..., (res) => { 
     // hoping this will run 
    }); 
} 

Dann Ihr Rückruf http.get() wird nie aufgerufen. Ihre While-Schleife bleibt für immer bestehen. Solange es läuft, kann der Callback von https.get() niemals aufgerufen werden. Diese Anfrage ist wahrscheinlich längst abgeschlossen und es gibt ein Ereignis, das in der internen JS-Ereigniswarteschlange sitzt, um den Rückruf aufzurufen, aber bis Ihre while() Schleife beendet ist, kann dieses Ereignis nicht aufgerufen werden. Sie haben also eine Sackgasse. Die while()-Schleife wartet auf etwas, das ausgeführt werden muss, um ihren Zustand zu ändern, aber nichts anderes kann ausgeführt werden, bis die while()-Schleife beendet ist.

Es gibt mehrere andere Möglichkeiten, serielle asynchrone Iterationen durchzuführen. Im Allgemeinen können Sie .forEach() oder while() nicht verwenden.

Hier sind mehrere Systeme für Asynchron-Looping:

Node.js: How do you handle callbacks in a loop?

While loop with jQuery async AJAX calls

How to synchronize a sequence of promises?

How to use after and each in conjunction to create a synchronous loop in underscore js

Oder die Asynchron-Bibliothek, die Sie auch Funktionen async hat erwähnt zu tun Schleife.

+0

Mehrere Referenzen zum Iterieren von asynchronen Operationen hinzugefügt. – jfriend00

+0

Dies sollte die akzeptierte Antwort sein. Ich verlasse meinen, nur für den Fall, dass jemand diese Frage durch Google tatsächlich nach dem Problem der Handhabung großer Json-Nutzlasten findet. – lorefnon

2

Zunächst einmal, ein paar MBs json Nutzlast ist nicht gerade riesig. Der Code des Routen-Handlers könnte daher einer genauen Prüfung bedürfen.

Um jedoch tatsächlich mit großen Mengen von JSON umzugehen, können Sie Ihre Anfrage als Stream konsumieren. JSONStream (zusammen mit vielen anderen ähnlichen Bibliotheken) können Sie dies auf eine speichereffiziente Weise tun. Sie können die zu verarbeitenden Pfade mit JSONPath (XPath analog für JSON) angeben und dann den Stream für übereinstimmende Datensätze abonnieren.

folgende Beispiel aus der README von JSONStream zeigt dies den Punkt:

var request = require('request') 
    , JSONStream = require('JSONStream') 
    , es = require('event-stream') 

request({url: 'http://isaacs.couchone.com/registry/_all_docs'}) 
    .pipe(JSONStream.parse('rows.*')) 
    .pipe(es.mapSync(function (data) { 
    console.error(data) 
    return data 
    })) 
+0

Es scheint nicht, dass große JSON tatsächlich das Problem hier überhaupt ist. Es ist eher ein Problem mit einer Endlosschleife und einer asynchronen Schleife. – jfriend00

0

den Strom Funktionalität des Anforderungsmodul verwenden große Mengen von ankommenden Daten zu verarbeiten. Wenn Daten durch den Stream kommen, analysieren Sie sie mit Daten, mit denen Sie arbeiten können, schieben Sie diese Daten durch die Pipe und ziehen Sie den nächsten Datenblock hinein.

Sie können einen Transformationsdatenstrom erstellen, um einen Teil der analysierten Daten zu bearbeiten, und einen Schreibdatenstrom, um den Datenblock zu speichern.

Zum Beispiel:

var stream = request ({ url: your_url }).pipe(parseStream) 
    .pipe(transformStream) 
    .pipe (writeStream); 

stream.on('finish',() => { 
    setImmediate (() => process.exit(0)); 
}); 

versuchen, für Informationen zum Erstellen von Streams https://bl.ocks.org/joyrexus/10026630

Verwandte Themen