La réponse courte est que seules les nouvelles données sont envoyées sur le fil. Voici comment ça fonctionne.
Trois parties importantes du serveur Meteor gèrent les abonnements: la fonction de publication , qui définit la logique des données fournies par l'abonnement; le pilote Mongo , qui surveille la base de données pour les changements; et la boîte de fusion , qui combine tous les abonnements actifs d'un client et les envoie sur le réseau au client.
Publier des fonctions
Chaque fois qu'un client Meteor s'abonne à une collection, le serveur exécute une
fonction de publication . Le travail de la fonction de publication est de déterminer l'ensemble de documents que son client doit avoir et d'envoyer chaque propriété de document dans la zone de fusion. Il s'exécute une fois pour chaque nouveau client abonné. Vous pouvez mettre n'importe quel JavaScript de votre choix dans la fonction de publication, comme un contrôle d'accès arbitrairement complexe à l'aide de this.userId
. La fonction de publication envoie des données dans la zone de fusion en appelant this.added
, this.changed
et
this.removed
. Consultez la
documentation de publication complète pour plus de détails.
La plupart des fonctions publication ne sont pas muck autour avec le bas niveau
added
, changed
et l' removed
API, cependant. Si une fonction retourne publier un curseur mongo, le serveur Meteor se connecte automatiquement la sortie du pilote Mongo ( insert
, update
et removed
callbacks) à l'entrée de la zone de fusion ( this.added
, this.changed
et this.removed
). Il est assez intéressant de pouvoir effectuer toutes les vérifications d'autorisation à l'avance dans une fonction de publication, puis de connecter directement le pilote de base de données à la boîte de fusion sans aucun code utilisateur. Et lorsque la publication automatique est activée, même ce petit élément est masqué: le serveur configure automatiquement une requête pour tous les documents de chaque collection et les pousse dans la zone de fusion.
En revanche, vous n'êtes pas limité à la publication de requêtes de base de données. Par exemple, vous pouvez écrire une fonction de publication qui lit une position GPS à partir d'un appareil à l'intérieur d'un Meteor.setInterval
ou interroge une API REST héritée à partir d'un autre service Web. Dans ces cas, vous auriez émettez des modifications à la boîte de fusion en appelant le bas niveau added
, changed
et removed
API DDP.
Le chauffeur Mongo
Le travail du pilote Mongo est de surveiller la base de données Mongo pour les modifications apportées aux requêtes en direct. Ces requêtes fonctionnent en continu et de retour mises à jour que les résultats changent en appelant added
, removed
et changed
callbacks.
Mongo n'est pas une base de données en temps réel. Alors le chauffeur fait un sondage. Il conserve une copie en mémoire du dernier résultat de la requête pour chaque requête active active. A chaque cycle de vote, il compare le nouveau résultat avec le résultat enregistré précédent, le calcul du jeu minimum de added
, removed
et les changed
événements qui décrivent la différence. Si plusieurs appelants enregistrent des rappels pour la même requête en direct, le pilote ne regarde qu'une copie de la requête, appelant chaque rappel enregistré avec le même résultat.
Chaque fois que le serveur met à jour une collection, le pilote recalcule chaque requête en direct sur cette collection (les versions futures de Meteor exposeront une API de mise à l'échelle pour limiter les requêtes en direct recalculées lors de la mise à jour.) Le pilote interroge également chaque requête en direct sur un minuteur de 10 secondes pour attraper les mises à jour de base de données hors bande qui ont contourné le serveur Meteor.
La boîte de fusion
Le travail de la boîte de fusion est de combiner les résultats ( added
, changed
et removed
appels) de toutes publier les fonctions actives d'un client en un seul flux de données. Il existe une boîte de fusion pour chaque client connecté. Il contient une copie complète du cache minimongo du client.
Dans votre exemple avec un seul abonnement, la zone de fusion est essentiellement un pass-through. Mais une application plus complexe peut avoir plusieurs abonnements qui peuvent se chevaucher. Si deux abonnements définissent tous deux le même attribut sur le même document, la zone de fusion décide quelle valeur est prioritaire et ne l'envoie qu'au client. Nous n'avons pas encore exposé l'API pour définir la priorité d'abonnement. Pour l'instant, la priorité est déterminée par l'ordre dans lequel le client souscrit aux ensembles de données. Le premier abonnement effectué par un client a la priorité la plus élevée, le deuxième abonnement est le suivant, et ainsi de suite.
Étant donné que la zone de fusion contient l'état du client, elle peut envoyer la quantité minimale de données pour maintenir chaque client à jour, quelle que soit la fonction de publication qui le nourrit.
Que se passe-t-il lors d'une mise à jour
Alors maintenant, nous avons préparé le terrain pour votre scénario.
Nous avons 1 000 clients connectés. Chacun est abonné à la même requête Mongo en direct ( Somestuff.find({})
). La requête étant la même pour chaque client, le pilote n'exécute qu'une seule requête en direct. Il y a 1 000 boîtes de fusion actives. Et chacun de clients publient la fonction a enregistré une added
, changed
et
removed
sur cette requête en direct qui alimente l' une des boîtes de fusion. Rien d'autre n'est connecté aux boîtes de fusion.
D'abord le pilote Mongo. Lorsqu'un des clients insère un nouveau document dans Somestuff
, il déclenche un recalcul. Le pilote Mongo réexécute la requête pour tous les documents dans Somestuff
, compare le résultat au résultat précédent en mémoire, trouve qu'il y a un nouveau document et appelle chacun des 1000 insert
rappels enregistrés .
Ensuite, les fonctions de publication. Il se passe très peu de choses ici: chacun des 1000 insert
rappels pousse les données dans la zone de fusion en appelant added
.
Enfin, chaque boîte de fusion vérifie ces nouveaux attributs par rapport à sa copie en mémoire du cache de son client. Dans chaque cas, il constate que les valeurs ne sont pas encore sur le client et n'observent pas une valeur existante. Ainsi, la boîte de fusion émet un DATA
message DDP sur la connexion SockJS à son client et met à jour sa copie en mémoire côté serveur.
Le coût total du processeur correspond au coût de diffraction d'une requête Mongo, plus le coût de 1000 boîtes de fusion vérifiant l'état de leurs clients et construisant une nouvelle charge utile de message DDP. Les seules données qui circulent sur le câble sont un seul objet JSON envoyé à chacun des 1000 clients, correspondant au nouveau document de la base de données, plus un message RPC au serveur du client qui a effectué l'insertion d'origine.
Optimisations
Voici ce que nous avons définitivement prévu.
Pilote Mongo plus efficace. Nous avons
optimisé le pilote
dans 0.5.1 pour n'exécuter qu'un seul observateur par requête distincte.
Toutes les modifications de base de données ne doivent pas déclencher un recalcul d'une requête. Nous pouvons apporter des améliorations automatisées, mais la meilleure approche est une API qui permet au développeur de spécifier les requêtes à réexécuter. Par exemple, il est évident pour un développeur que l'insertion d'un message dans un salon de discussion ne doit pas invalider une requête en direct pour les messages d'une seconde salle.
Le pilote Mongo, la fonction de publication et la zone de fusion n'ont pas besoin de s'exécuter dans le même processus, ni même sur la même machine. Certaines applications exécutent des requêtes en direct complexes et nécessitent plus de processeur pour surveiller la base de données. D'autres n'ont que quelques requêtes distinctes (imaginez un moteur de blog), mais peut-être de nombreux clients connectés - ceux-ci ont besoin de plus de CPU pour les boîtes de fusion. Séparer ces composants nous permettra de mettre à l'échelle chaque pièce indépendamment.
De nombreuses bases de données prennent en charge les déclencheurs qui se déclenchent lorsqu'une ligne est mise à jour et fournissent les anciennes et les nouvelles lignes. Avec cette fonctionnalité, un pilote de base de données pourrait enregistrer un déclencheur au lieu d'interroger les modifications.
skip
,$near
et$where
contenant des requêtes) qui est beaucoup plus efficace de la charge CPU, bande passante réseau et permet la montée en puissance l' application les serveurs.D'après mon expérience, utiliser de nombreux clients avec tout en partageant une énorme collection dans Meteor est pratiquement impossible, à partir de la version 0.7.0.1. J'essaierai d'expliquer pourquoi.
Comme décrit dans le post ci-dessus et également sur https://github.com/meteor/meteor/issues/1821 , le serveur meteor doit conserver une copie des données publiées pour chaque client dans la zone de fusion . C'est ce qui permet à la magie de Meteor de se produire, mais cela entraîne également le maintien de toutes les grandes bases de données partagées dans la mémoire du processus de nœud. Même en utilisant une optimisation possible pour des collections statiques comme dans ( Y a-t-il un moyen de dire à meteor qu'une collection est statique (ne changera jamais)? ), Nous avons rencontré un énorme problème avec l'utilisation du processeur et de la mémoire du processus Node.
Dans notre cas, nous publions une collection de 15 000 documents pour chaque client qui était complètement statique. Le problème est que la copie de ces documents dans la boîte de fusion d'un client (en mémoire) lors de la connexion a essentiellement amené le processus Node à 100% du processeur pendant près d'une seconde et a entraîné une utilisation supplémentaire importante de la mémoire. Ceci est intrinsèquement non évolutif, car tout client qui se connecte mettra le serveur à genoux (et les connexions simultanées se bloqueront mutuellement) et l'utilisation de la mémoire augmentera linéairement dans le nombre de clients. Dans notre cas, chaque client a entraîné une utilisation supplémentaire d' environ 60 Mo de mémoire, même si les données brutes transférées ne représentaient que 5 Mo environ.
Dans notre cas, comme la collection était statique, nous avons résolu ce problème en envoyant tous les documents sous forme de
.json
fichier, qui a été gzippé par nginx, et en les chargeant dans une collection anonyme, ce qui n'a entraîné qu'un transfert d'environ 1 Mo de données sans processeur supplémentaire. ou de la mémoire dans le processus de nœud et un temps de chargement beaucoup plus rapide. Toutes les opérations sur cette collection ont été effectuées en utilisant des_id
s de publications beaucoup plus petites sur le serveur, ce qui permet de conserver la plupart des avantages de Meteor. Cela a permis à l'application de s'adapter à beaucoup plus de clients. De plus, comme notre application est principalement en lecture seule, nous avons encore amélioré l'évolutivité en exécutant plusieurs instances Meteor derrière nginx avec équilibrage de charge (mais avec un seul Mongo), car chaque instance de Node est monothread.Cependant, le problème du partage de grandes collections inscriptibles entre plusieurs clients est un problème d'ingénierie qui doit être résolu par Meteor. Il existe probablement un meilleur moyen que de conserver une copie de tout pour chaque client, mais cela nécessite une réflexion approfondie en tant que problème de systèmes distribués. Les problèmes actuels d'utilisation massive du processeur et de la mémoire ne peuvent tout simplement pas évoluer.
la source
L'expérience que vous pouvez utiliser pour répondre à cette question:
meteor create --example todos
Pour obtenir des conseils sur l'utilisation de WKI, consultez cet article . C'est un peu dépassé, mais surtout toujours valable, surtout pour cette question.
la source
Cela fait encore un an maintenant et je pense donc que les connaissances antérieures à "Meteor 1.0", donc les choses ont peut-être encore changé? Je suis toujours à la recherche de cela. http://meteorhacks.com/does-meteor-scale.html mène à un "Comment mettre à l'échelle Meteor?" article http://meteorhacks.com/how-to-scale-meteor.html
la source