La respuesta corta es que solo se envían nuevos datos por el cable. Así es como funciona.
Hay tres partes importantes del servidor Meteor que administran las suscripciones: la función de publicación , que define la lógica de los datos que proporciona la suscripción; el controlador Mongo , que vigila la base de datos en busca de cambios; y el cuadro de combinación , que combina todas las suscripciones activas de un cliente y las envía a través de la red al cliente.
Publicar funciones
Cada vez que un cliente de Meteor se suscribe a una colección, el servidor ejecuta una
función de publicación . El trabajo de la función de publicación es averiguar el conjunto de documentos que su cliente debe tener y enviar cada propiedad del documento al cuadro de combinación. Se ejecuta una vez por cada nuevo cliente suscrito. Puede poner cualquier JavaScript que desee en la función de publicación, como el uso de control de acceso arbitrariamente complejo this.userId
. El publican función envía los datos en la caja de combinación llamando this.added
, this.changed
y
this.removed
. Consulte la
documentación de publicación completa para obtener más detalles.
La mayoría de publicar funciones no tienen que ensuciar alrededor con el bajo nivel
added
, changed
y la removed
API, sin embargo. Si una publican función devuelve un cursor Mongo, el servidor Meteor conecta automáticamente la salida del controlador de Mongo ( insert
, update
y removed
devoluciones de llamada) a la entrada de la caja de combinación ( this.added
, this.changed
y this.removed
). Es bastante bueno que pueda hacer todas las comprobaciones de permisos por adelantado en una función de publicación y luego conectar directamente el controlador de la base de datos al cuadro de combinación sin ningún código de usuario en el camino. Y cuando la publicación automática está activada, incluso esta pequeña parte está oculta: el servidor configura automáticamente una consulta para todos los documentos de cada colección y los inserta en el cuadro de combinación.
Por otro lado, no está limitado a publicar consultas de bases de datos. Por ejemplo, puede escribir una función de publicación que lea una posición GPS de un dispositivo dentro de un Meteor.setInterval
, o sondee una API REST heredada de otro servicio web. En esos casos, usted emiten cambios en el cuadro de combinación llamando al bajo nivel added
, changed
y removed
DDP API.
El conductor de Mongo
El trabajo del conductor de Mongo es observar la base de datos de Mongo para ver si hay cambios en las consultas en vivo. Estas consultas se ejecuten de forma continua y actualizaciones a medida que cambian los resultados vuelven llamando added
, removed
y changed
las devoluciones de llamada.
Mongo no es una base de datos en tiempo real. Así que el conductor realiza encuestas. Mantiene una copia en memoria del último resultado de la consulta para cada consulta activa en vivo. En cada ciclo de sondeo, se compara el nuevo resultado con el resultado guardado anterior, calcular el conjunto mínimo de added
, removed
y changed
acontecimientos que describen la diferencia. Si varias personas que llaman registran devoluciones de llamada para la misma consulta en vivo, el conductor solo ve una copia de la consulta, llamando a cada devolución de llamada registrada con el mismo resultado.
Cada vez que el servidor actualiza una colección, el controlador vuelve a calcular cada consulta en vivo en esa colección (las versiones futuras de Meteor expondrán una API de escalado para limitar qué consultas en vivo se recalculan en la actualización). El controlador también sondea cada consulta en vivo en un temporizador de 10 segundos para detectar actualizaciones de base de datos fuera de banda que eludieron el servidor Meteor.
El cuadro de combinación
El trabajo del cuadro de combinación es la combinación de los resultados ( added
, changed
y removed
llamadas) de todas las funciones publicar activos de un cliente en un único flujo de datos. Hay un cuadro de combinación para cada cliente conectado. Contiene una copia completa de la caché minimongo del cliente.
En su ejemplo con una sola suscripción, el cuadro de combinación es esencialmente una transferencia. Pero una aplicación más compleja puede tener varias suscripciones que pueden superponerse. Si dos suscripciones establecen el mismo atributo en el mismo documento, el cuadro de combinación decide qué valor tiene prioridad y solo lo envía al cliente. Aún no hemos expuesto la API para establecer la prioridad de suscripción. Por ahora, la prioridad está determinada por el orden que el cliente suscribe a los conjuntos de datos. La primera suscripción que realiza un cliente tiene la prioridad más alta, la segunda suscripción es la siguiente más alta, y así sucesivamente.
Debido a que el cuadro de combinación contiene el estado del cliente, puede enviar la cantidad mínima de datos para mantener a cada cliente actualizado, sin importar la función de publicación que lo alimente.
Qué sucede en una actualización
Así que ahora hemos preparado el escenario para su escenario.
Tenemos 1.000 clientes conectados. Cada uno está suscrito a la misma consulta de Mongo en vivo ( Somestuff.find({})
). Dado que la consulta es la misma para cada cliente, el controlador solo ejecuta una consulta en vivo. Hay 1000 cuadros de combinación activos. Y la función de publicación de cada cliente registró un added
, changed
y
removed
en esa consulta en vivo que se alimenta en uno de los cuadros de combinación. Nada más está conectado a los cuadros de combinación.
Primero el controlador de Mongo. Cuando uno de los clientes inserta un nuevo documento en Somestuff
, se activa un nuevo cálculo. El controlador de Mongo vuelve a ejecutar la consulta para todos los documentos en Somestuff
, compara el resultado con el resultado anterior en la memoria, encuentra que hay un documento nuevo y llama a cada una de las 1,000 insert
devoluciones de llamada registradas .
A continuación, las funciones de publicación. Aquí sucede muy poco: cada una de las 1,000 insert
devoluciones de llamada empuja los datos al cuadro de combinación mediante una llamada added
.
Por último, cada cuadro de combinación comprueba estos nuevos atributos con su copia en memoria de la caché de su cliente. En cada caso, encuentra que los valores aún no están en el cliente y no sombrean un valor existente. Entonces, el cuadro de combinación emite un DATA
mensaje DDP en la conexión SockJS a su cliente y actualiza su copia en memoria del lado del servidor.
El costo total de la CPU es el costo de diferenciar una consulta de Mongo, más el costo de 1,000 cuadros de combinación que verifican el estado de sus clientes y construyen una nueva carga útil de mensajes DDP. Los únicos datos que fluyen a través del cable son un único objeto JSON enviado a cada uno de los 1,000 clientes, correspondiente al nuevo documento en la base de datos, más un mensaje RPC al servidor desde el cliente que hizo la inserción original.
Optimizaciones
Esto es lo que definitivamente hemos planeado.
Conductor Mongo más eficiente. Hemos
optimizado el controlador
en 0.5.1 sólo para ejecutar un solo observador por consulta distinta.
No todos los cambios en la base de datos deberían desencadenar un nuevo cálculo de una consulta. Podemos realizar algunas mejoras automatizadas, pero el mejor enfoque es una API que permite al desarrollador especificar qué consultas deben volver a ejecutarse. Por ejemplo, es obvio para un desarrollador que insertar un mensaje en una sala de chat no debería invalidar una consulta en vivo para los mensajes en una segunda sala.
El controlador de Mongo, la función de publicación y el cuadro de combinación no necesitan ejecutarse en el mismo proceso, ni siquiera en la misma máquina. Algunas aplicaciones ejecutan consultas complejas en vivo y necesitan más CPU para observar la base de datos. Otros tienen solo unas pocas consultas distintas (imagine un motor de blog), pero posiblemente muchos clientes conectados, estos necesitan más CPU para los cuadros de combinación. Separar estos componentes nos permitirá escalar cada pieza de forma independiente.
Muchas bases de datos admiten activadores que se activan cuando se actualiza una fila y proporcionan las filas nuevas y antiguas. Con esa función, un controlador de base de datos podría registrar un disparador en lugar de sondear los cambios.