Die kurze Antwort lautet, dass nur neue Daten über das Kabel gesendet werden. So funktioniert das.
Es gibt drei wichtige Teile des Meteor-Servers, die Abonnements verwalten: die Veröffentlichungsfunktion , die die Logik für die vom Abonnement bereitgestellten Daten definiert; der Mongo-Treiber , der die Datenbank auf Änderungen überwacht; und das Zusammenführungsfeld , in dem alle aktiven Abonnements eines Clients zusammengefasst und über das Netzwerk an den Client gesendet werden.
Funktionen veröffentlichen
Jedes Mal, wenn ein Meteor-Client eine Sammlung abonniert, führt der Server eine
Veröffentlichungsfunktion aus . Die Aufgabe der Veröffentlichungsfunktion besteht darin, den Satz von Dokumenten herauszufinden, über den der Client verfügen sollte, und jede Dokumenteigenschaft in das Zusammenführungsfeld zu senden. Es wird einmal für jeden neuen abonnierenden Client ausgeführt. Sie können beliebiges JavaScript in die Veröffentlichungsfunktion einfügen, z. B. eine beliebig komplexe Zugriffssteuerung mit this.userId
. Die veröffentlichen Funktionsdaten in die Mergebox senden durch den Aufruf this.added
, this.changed
und
this.removed
. Weitere Informationen finden Sie in der
vollständigen Veröffentlichungsdokumentation .
Die meisten veröffentlichen Funktionen müssen mit dem Low-Level nicht verarschen
added
, changed
und removed
API, though. Wenn eine Funktion gibt einen Mongo Cursor veröffentlicht, verbindet der Meteor - Server automatisch die Ausgabe des Mongo Treiber ( insert
, update
und removed
Rückrufe) mit dem Eingang der Merge - Box ( this.added
, this.changed
und this.removed
). Es ist ziemlich ordentlich, dass Sie alle Berechtigungsprüfungen in einer Veröffentlichungsfunktion im Voraus durchführen und dann den Datenbanktreiber direkt mit dem Zusammenführungsfeld verbinden können, ohne dass ein Benutzercode im Weg steht. Und wenn die automatische Veröffentlichung aktiviert ist, wird auch dieses kleine bisschen ausgeblendet: Der Server richtet automatisch eine Abfrage für alle Dokumente in jeder Sammlung ein und schiebt sie in das Zusammenführungsfeld.
Andererseits sind Sie nicht darauf beschränkt, Datenbankabfragen zu veröffentlichen. Sie können beispielsweise eine Veröffentlichungsfunktion schreiben, die eine GPS-Position von einem Gerät in einem liest Meteor.setInterval
oder eine ältere REST-API von einem anderen Webdienst abfragt. In diesen Fällen würden Sie Änderungen an der Mergebox emittieren durch die Low-Level - Aufruf added
, changed
und removed
DDP - API.
Der Mongo-Fahrer
Die Aufgabe des Mongo-Treibers besteht darin, die Mongo-Datenbank auf Änderungen an Live-Abfragen zu überwachen. Diese Abfragen laufen kontinuierlich und Rück Updates wie die Ergebnisse Änderung durch den Aufruf added
, removed
und changed
Rückrufe.
Mongo ist keine Echtzeitdatenbank. Also fragt der Fahrer ab. Es speichert eine speicherinterne Kopie des letzten Abfrageergebnisses für jede aktive Live-Abfrage. An jedem Abfragezyklus vergleicht es das neue Ergebnis mit dem vorherigen gespeicherten Ergebnis der Berechnung des Mindestsatzes an added
, removed
und changed
Ereignisse, die den Unterschied beschreiben. Wenn mehrere Anrufer Rückrufe für dieselbe Live-Abfrage registrieren, überwacht der Treiber nur eine Kopie der Abfrage und ruft jeden registrierten Rückruf mit demselben Ergebnis auf.
Jedes Mal, wenn der Server eine Sammlung aktualisiert, berechnet der Treiber jede Live-Abfrage in dieser Sammlung neu (zukünftige Versionen von Meteor stellen eine Skalierungs-API zur Verfügung, um zu begrenzen, welche Live-Abfragen bei der Aktualisierung neu berechnet werden.) Der Treiber fragt auch jede Live-Abfrage in einem 10-Sekunden-Timer ab Abrufen von Out-of-Band-Datenbankaktualisierungen, die den Meteor-Server umgangen haben.
Das Zusammenführungsfeld
Die Aufgabe der Merge - Box ist , die Ergebnisse zu kombinieren ( added
, changed
und removed
Anrufe) alle ein aktiven veröffentlichen Funktionen in einen einzigen Datenstrom des Kunden. Für jeden verbundenen Client gibt es ein Zusammenführungsfeld. Es enthält eine vollständige Kopie des Minimongo-Cache des Clients.
In Ihrem Beispiel mit nur einem Abonnement ist das Zusammenführungsfeld im Wesentlichen ein Durchgang. Eine komplexere App kann jedoch mehrere Abonnements haben, die sich möglicherweise überschneiden. Wenn zwei Abonnements dasselbe Attribut für dasselbe Dokument festlegen, entscheidet das Zusammenführungsfeld, welcher Wert Priorität hat, und sendet diesen nur an den Client. Wir haben die API zum Festlegen der Abonnementpriorität noch nicht verfügbar gemacht. Derzeit wird die Priorität durch die Reihenfolge bestimmt, in der der Client Datensätze abonniert. Das erste Abonnement, das ein Client abschließt, hat die höchste Priorität, das zweite Abonnement hat die nächsthöhere Priorität und so weiter.
Da das Zusammenführungsfeld den Status des Clients enthält, kann es die Mindestdatenmenge senden, um jeden Client auf dem neuesten Stand zu halten, unabhängig davon, welche Veröffentlichungsfunktion ihn speist.
Was passiert bei einem Update?
Jetzt haben wir die Voraussetzungen für Ihr Szenario geschaffen.
Wir haben 1.000 verbundene Kunden. Jeder hat dieselbe Live-Mongo-Abfrage ( Somestuff.find({})
) abonniert . Da die Abfrage für jeden Client gleich ist, führt der Treiber nur eine Live-Abfrage aus. Es gibt 1.000 aktive Zusammenführungsfelder. Die Veröffentlichungsfunktion jedes Clients registrierte eine added
, changed
und
removed
in dieser Live-Abfrage, die in eines der Zusammenführungsfelder eingespeist wird. Mit den Zusammenführungsfeldern ist nichts anderes verbunden.
Zuerst der Mongo-Fahrer. Wenn einer der Clients ein neues Dokument einfügt Somestuff
, wird eine Neuberechnung ausgelöst. Der Mongo-Treiber führt die Abfrage für alle Dokumente erneut aus Somestuff
, vergleicht das Ergebnis mit dem vorherigen Ergebnis im Speicher, stellt fest, dass ein neues Dokument vorhanden ist, und ruft jeden der 1.000 registrierten insert
Rückrufe auf.
Als nächstes werden die Veröffentlichungsfunktionen ausgeführt. Hier passiert sehr wenig: Jeder der 1.000 insert
Rückrufe schiebt Daten durch Aufrufen in die Zusammenführungsbox added
.
Schließlich vergleicht jedes Zusammenführungsfeld diese neuen Attribute mit seiner speicherinternen Kopie des Client-Cache. In jedem Fall wird festgestellt, dass die Werte noch nicht auf dem Client vorhanden sind und keinen vorhandenen Wert beschatten. Das Merge-Box sendet also eine DDP- DATA
Nachricht über die SockJS-Verbindung an seinen Client und aktualisiert seine serverseitige In-Memory-Kopie.
Die Gesamt-CPU-Kosten sind die Kosten für die Differenzierung einer Mongo-Abfrage sowie die Kosten für 1.000 Zusammenführungsfelder, die den Status ihrer Clients überprüfen und eine neue Nutzlast für DDP-Nachrichten erstellen. Die einzigen Daten, die über die Leitung fließen, sind ein einzelnes JSON-Objekt, das an jeden der 1.000 Clients gesendet wird und dem neuen Dokument in der Datenbank entspricht, sowie eine RPC-Nachricht an den Server vom Client, der die ursprüngliche Einfügung vorgenommen hat.
Optimierungen
Folgendes haben wir definitiv geplant.
Effizienterer Mongo-Fahrer. Wir haben
den Treiber
in 0.5.1 so optimiert , dass nur ein einziger Beobachter pro eindeutiger Abfrage ausgeführt wird.
Nicht jede DB-Änderung sollte eine Neuberechnung einer Abfrage auslösen. Wir können einige automatisierte Verbesserungen vornehmen, aber der beste Ansatz ist eine API, mit der der Entwickler angeben kann, welche Abfragen erneut ausgeführt werden müssen. Für einen Entwickler ist es beispielsweise offensichtlich, dass das Einfügen einer Nachricht in einen Chatroom eine Live-Abfrage für die Nachrichten in einem zweiten Raum nicht ungültig machen sollte.
Der Mongo-Treiber, die Veröffentlichungsfunktion und das Zusammenführungsfeld müssen nicht im selben Prozess oder sogar auf demselben Computer ausgeführt werden. Einige Anwendungen führen komplexe Live-Abfragen aus und benötigen mehr CPU, um die Datenbank zu überwachen. Andere haben nur wenige unterschiedliche Abfragen (stellen Sie sich eine Blog-Engine vor), aber möglicherweise viele verbundene Clients - diese benötigen mehr CPU für Merge-Boxen. Durch die Trennung dieser Komponenten können wir jedes Stück unabhängig skalieren.
Viele Datenbanken unterstützen Trigger, die beim Aktualisieren einer Zeile ausgelöst werden und die alten und neuen Zeilen bereitstellen. Mit dieser Funktion könnte ein Datenbanktreiber einen Trigger registrieren, anstatt nach Änderungen abzufragen.
skip
,$near
und$where
enthalten Abfragen) , die wesentlich effizienter in der CPU - Auslastung, Netzwerk - Bandbreite und ermöglicht Anwendung horizontale Skalierung Server.Nach meiner Erfahrung ist die Verwendung vieler Clients mit einer großen Sammlung in Meteor ab Version 0.7.0.1 im Wesentlichen nicht mehr möglich. Ich werde versuchen zu erklären warum.
Wie im obigen Beitrag und auch unter https://github.com/meteor/meteor/issues/1821 beschrieben , muss der Meteorserver eine Kopie der veröffentlichten Daten für jeden Client im Zusammenführungsfeld aufbewahren . Dies ermöglicht die Meteor-Magie, führt aber auch dazu, dass große gemeinsam genutzte Datenbanken wiederholt im Speicher des Knotenprozesses gespeichert werden. Selbst bei Verwendung einer möglichen Optimierung für statische Sammlungen wie in ( Gibt es eine Möglichkeit, Meteor mitzuteilen, dass eine Sammlung statisch ist (sich nie ändern wird? ) ? ) Trat ein großes Problem mit der CPU- und Speichernutzung des Knotenprozesses auf.
In unserem Fall haben wir für jeden Kunden eine Sammlung von 15.000 Dokumenten veröffentlicht, die vollständig statisch war. Das Problem besteht darin, dass das Kopieren dieser Dokumente in die Zusammenführungsbox eines Clients (im Speicher) bei der Verbindung den Knotenprozess für fast eine Sekunde auf 100% CPU brachte und zu einer großen zusätzlichen Speichernutzung führte. Dies ist von Natur aus nicht skalierbar, da jeder verbindende Client den Server in die Knie zwingt (und gleichzeitige Verbindungen sich gegenseitig blockieren) und die Speichernutzung in der Anzahl der Clients linear ansteigt. In unserem Fall verursachte jeder Client eine zusätzliche Speicherauslastung von ~ 60 MB , obwohl die übertragenen Rohdaten nur etwa 5 MB betrugen.
In unserem Fall haben wir dieses Problem gelöst, da die Sammlung statisch war, indem wir alle Dokumente als
.json
Datei gesendet haben, die von nginx komprimiert wurde, und sie in eine anonyme Sammlung geladen haben, was nur zu einer Datenübertragung von ~ 1 MB ohne zusätzliche CPU führte oder Speicher im Knotenprozess und eine viel schnellere Ladezeit. Alle Operationen über diese Sammlung wurden unter Verwendung von_id
s aus viel kleineren Veröffentlichungen auf dem Server durchgeführt, wodurch die meisten Vorteile von Meteor beibehalten wurden. Dadurch konnte die App auf viele weitere Clients skaliert werden. Da unsere App größtenteils schreibgeschützt ist, haben wir die Skalierbarkeit weiter verbessert, indem wir mehrere Meteor-Instanzen hinter nginx mit Lastenausgleich (allerdings mit einem einzelnen Mongo) ausgeführt haben, da jede Node-Instanz Single-Threaded ist.Das Problem, große, beschreibbare Sammlungen für mehrere Clients freizugeben, ist jedoch ein technisches Problem, das von Meteor gelöst werden muss. Es gibt wahrscheinlich einen besseren Weg, als für jeden Client eine Kopie von allem aufzubewahren, aber dies erfordert einige ernsthafte Überlegungen als Problem mit verteilten Systemen. Die aktuellen Probleme der massiven CPU- und Speicherauslastung lassen sich einfach nicht skalieren.
quelle
Das Experiment, mit dem Sie diese Frage beantworten können:
meteor create --example todos
Tipps zur Verwendung von WKI finden Sie in diesem Artikel . Es ist etwas veraltet, aber meistens noch gültig, besonders für diese Frage.
quelle
Dies ist noch ein Jahr alt und daher denke ich, dass ich vor "Meteor 1.0" weiß, also haben sich die Dinge möglicherweise wieder geändert? Ich untersuche das immer noch. http://meteorhacks.com/does-meteor-scale.html führt zu einem "Wie skaliere ich Meteor?" Artikel http://meteorhacks.com/how-to-scale-meteor.html
quelle