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.