La risposta breve è che solo i nuovi dati vengono inviati lungo il filo. Ecco come funziona.
Ci sono tre parti importanti del server Meteor che gestiscono gli abbonamenti: la funzione di pubblicazione , che definisce la logica per i dati forniti dall'abbonamento; il driver Mongo , che controlla le modifiche nel database; e la casella di unione , che combina tutti gli abbonamenti attivi di un client e li invia tramite la rete al client.
Pubblica funzioni
Ogni volta che un client Meteor si iscrive a una raccolta, il server esegue una
funzione di pubblicazione . Il compito della funzione di pubblicazione è individuare il set di documenti che il suo cliente dovrebbe avere e inviare ciascuna proprietà del documento nella casella di unione. Viene eseguito una volta per ogni nuovo client in abbonamento. Puoi inserire qualsiasi JavaScript che desideri nella funzione di pubblicazione, come il controllo degli accessi arbitrariamente complesso utilizzando this.userId
. La funzione di pubblicare invia i dati nella casella di merge chiamando this.added
, this.changed
e
this.removed
. Consulta la
documentazione di pubblicazione completa per maggiori dettagli.
La maggior parte delle funzioni di pubblicare non devono muck in giro con il basso livello
added
, changed
e removed
API, però. Se un pubblicano funzione restituisce un cursore Mongo, il server Meteor connette automaticamente l'uscita del driver Mongo ( insert
, update
e removed
callback) all'ingresso della scatola di unione ( this.added
, this.changed
e this.removed
). È abbastanza chiaro che puoi eseguire tutti i controlli dei permessi in anticipo in una funzione di pubblicazione e quindi connettere direttamente il driver del database alla casella di unione senza alcun codice utente nel modo. E quando la pubblicazione automatica è attiva, anche questa piccola parte è nascosta: il server imposta automaticamente una query per tutti i documenti in ogni raccolta e li inserisce nella casella di unione.
D'altra parte, non sei limitato alla pubblicazione di query di database. Ad esempio, è possibile scrivere una funzione di pubblicazione che legge una posizione GPS da un dispositivo all'interno di una Meteor.setInterval
o esegue il polling di un'API REST legacy da un altro servizio Web. In questi casi, ci si emettono modifiche alla casella di merge chiamando il basso livello added
, changed
e removed
DDP API.
L'autista Mongo
Il compito del conducente Mongo è controllare il database Mongo per le modifiche alle query in tempo reale. Queste query eseguite in modo continuo e restituiscono gli aggiornamenti come il cambiamento risultati chiamando added
, removed
e changed
callback.
Mongo non è un database in tempo reale. Quindi l'autista fa i sondaggi. Conserva una copia in memoria del risultato dell'ultima query per ogni query live attiva. Su ogni ciclo di polling, si confronta il nuovo risultato con il risultato precedente salvato, calcolando l'insieme minimo di added
, removed
e changed
gli eventi che descrivono la differenza. Se più chiamanti registrano i callback per la stessa query live, il driver guarda solo una copia della query, chiamando ogni callback registrata con lo stesso risultato.
Ogni volta che il server aggiorna una raccolta, il driver ricalcola ogni query in tempo reale su quella raccolta (le versioni future di Meteor esporranno un'API di ridimensionamento per limitare le query in tempo reale che vengono ricalcolate durante l'aggiornamento). Il driver inoltre esegue il polling di ciascuna query in tempo reale su un rilevare gli aggiornamenti del database fuori banda che hanno ignorato il server Meteor.
La scatola di fusione
Il lavoro della scatola di fusione è quello di combinare i risultati ( added
, changed
e removed
le chiamate) di tutte le funzioni pubblicare attivi di un cliente in un unico flusso di dati. C'è una casella di unione per ogni client connesso. Contiene una copia completa della cache minimongo del client.
Nel tuo esempio con un solo abbonamento, la casella di unione è essenzialmente un pass-through. Ma un'app più complessa può avere più abbonamenti che potrebbero sovrapporsi. Se due sottoscrizioni impostano entrambe lo stesso attributo sullo stesso documento, la casella di unione decide quale valore ha la priorità e lo invia solo al client. Non abbiamo ancora esposto l'API per l'impostazione della priorità dell'abbonamento. Per ora, la priorità è determinata dall'ordine in cui il client sottoscrive i set di dati. La prima sottoscrizione effettuata da un client ha la massima priorità, la seconda è la successiva più alta e così via.
Poiché la casella di unione mantiene lo stato del client, può inviare la quantità minima di dati per mantenere ogni client aggiornato, indipendentemente dalla funzione di pubblicazione che lo alimenta.
Cosa succede con un aggiornamento
Quindi ora abbiamo preparato il terreno per il tuo scenario.
Abbiamo 1.000 clienti connessi. Ciascuno è iscritto alla stessa query Mongo dal vivo ( Somestuff.find({})
). Poiché la query è la stessa per ogni client, il driver esegue solo una query live. Sono presenti 1.000 caselle di unione attive. E la funzione di pubblicazione di ogni cliente ha registrato un added
, changed
e
removed
su quella query in tempo reale che alimenta una delle caselle di unione. Nient'altro è collegato alle caselle di unione.
Per prima cosa l'autista Mongo. Quando uno dei client inserisce un nuovo documento in Somestuff
, attiva un ricalcolo. Il driver Mongo riesegue la query per tutti i documenti in Somestuff
, confronta il risultato con il risultato precedente in memoria, rileva che è presente un nuovo documento e chiama ciascuno dei 1.000 insert
callback registrati .
Successivamente, le funzioni di pubblicazione. Qui sta succedendo molto poco: ognuno dei 1.000 insert
callback spinge i dati nella casella di unione chiamando added
.
Infine, ogni casella di unione controlla questi nuovi attributi rispetto alla sua copia in memoria della cache del suo client. In ogni caso, rileva che i valori non sono ancora presenti sul client e non nascondono un valore esistente. Quindi la casella di unione emette un DATA
messaggio DDP sulla connessione SockJS al suo client e aggiorna la sua copia in memoria lato server.
Il costo totale della CPU è il costo per diff una query Mongo, più il costo di 1.000 caselle di unione che controllano lo stato dei loro clienti e costruiscono un nuovo payload del messaggio DDP. Gli unici dati che fluiscono sulla rete sono un singolo oggetto JSON inviato a ciascuno dei 1.000 client, corrispondente al nuovo documento nel database, più un messaggio RPC al server dal client che ha effettuato l'inserimento originale.
Ottimizzazioni
Ecco cosa abbiamo decisamente pianificato.
Driver Mongo più efficiente. Abbiamo
ottimizzato il driver
in 0.5.1 per eseguire un solo osservatore per query distinta.
Non tutte le modifiche al database dovrebbero attivare un ricalcolo di una query. Possiamo apportare alcuni miglioramenti automatici, ma l'approccio migliore è un'API che consente allo sviluppatore di specificare quali query devono essere rieseguite. Ad esempio, è ovvio per uno sviluppatore che l'inserimento di un messaggio in una chatroom non dovrebbe invalidare una query in tempo reale per i messaggi in una seconda stanza.
Il driver Mongo, la funzione di pubblicazione e la casella di unione non devono essere eseguiti nello stesso processo o anche sulla stessa macchina. Alcune applicazioni eseguono query live complesse e necessitano di più CPU per controllare il database. Altri hanno solo poche query distinte (immagina un motore di blog), ma forse molti client connessi: questi richiedono più CPU per le caselle di unione. Separare questi componenti ci consentirà di ridimensionare ogni pezzo in modo indipendente.
Molti database supportano trigger che si attivano quando una riga viene aggiornata e forniscono le righe vecchie e nuove. Con questa funzione, un driver di database potrebbe registrare un trigger invece di eseguire il polling per le modifiche.