Короткий ответ: по сети отправляются только новые данные. Вот как это работает.
Есть три важных части сервера Meteor, которые управляют подписками: функция публикации , которая определяет логику того, какие данные предоставляет подписка; водитель Монго , который следит за базой данных для изменений; и поле слияния , которое объединяет все активные подписки клиента и отправляет их клиенту по сети.
Функции публикации
Каждый раз, когда клиент Meteor подписывается на коллекцию, сервер выполняет функцию
публикации . Задача функции публикации - определить набор документов, который должен иметь клиент, и отправить каждое свойство документа в поле слияния. Он запускается один раз для каждого нового подписавшегося клиента. Вы можете поместить любой JavaScript в функцию публикации, например, произвольно сложный контроль доступа с использованием this.userId
. Функция публикации отправляет данные в поле слияния, вызывая this.added
, this.changed
и
this.removed
. См.
Полную документацию по публикации для получения более подробной информации.
Большинство публиковать функции не слоняться вокруг с низким уровнем
added
, changed
и removed
API, хотя. Если опубликовать функция возвращает Монго курсора, сервер Метеор автоматически подключает выход драйвера Монго ( insert
, update
и removed
обратные вызовы) на вход коробки слияния ( this.added
, this.changed
и this.removed
). Довольно удобно, что вы можете выполнить все проверки разрешений заранее в функции публикации, а затем напрямую подключить драйвер базы данных к блоку слияния без какого-либо пользовательского кода. А когда автопубликация включена, даже эта небольшая часть скрыта: сервер автоматически устанавливает запрос для всех документов в каждой коллекции и помещает их в поле слияния.
С другой стороны, вы не ограничены публикацией запросов к базе данных. Например, вы можете написать функцию публикации, которая считывает положение GPS с устройства внутри Meteor.setInterval
или опрашивает устаревший REST API из другой веб-службы. В тех случаях, вы испускаете изменения в поле слияния, вызвав низкий уровень added
, changed
и removed
DDP API.
Драйвер Mongo
Задача драйвера Mongo - следить за базой данных Mongo на предмет изменений в живых запросах. Эти запросы непрерывно работать и возвращать обновления , как изменение результатов по телефону added
, removed
и changed
обратных вызовов.
Mongo - это не база данных в реальном времени. Так что водитель опрашивает. Он сохраняет в памяти копию результата последнего запроса для каждого активного запроса в реальном времени. На каждом цикле опроса, он сравнивает новый результат с предыдущим сохраненным результатом, вычислением минимального набора added
, removed
и changed
событий , которые описывают разницу. Если несколько вызывающих абонентов регистрируют обратные вызовы для одного и того же оперативного запроса, драйвер просматривает только одну копию запроса, вызывая каждый зарегистрированный обратный вызов с одним и тем же результатом.
Каждый раз, когда сервер обновляет коллекцию, драйвер пересчитывает каждый живой запрос в этой коллекции (будущие версии Meteor будут предоставлять API масштабирования для ограничения того, какие живые запросы пересчитываются при обновлении). Драйвер также опрашивает каждый живой запрос по 10-секундному таймеру, чтобы перехватить обновления базы данных по внеполосному каналу, которые обошли сервер Meteor.
Поле слияния
Работа в поле слияния является объединение результатов ( added
, changed
и removed
звонки) всех активных публикующих функций организма клиента в единый поток данных. Для каждого подключенного клиента есть одно поле слияния. Он содержит полную копию клиентского кэша Minimongo.
В вашем примере с единственной подпиской поле слияния по сути является сквозным. Но более сложное приложение может иметь несколько подписок, которые могут перекрываться. Если две подписки устанавливают один и тот же атрибут в одном и том же документе, поле слияния решает, какое значение имеет приоритет, и отправляет его только клиенту. Мы еще не представили API для установки приоритета подписки. На данный момент приоритет определяется порядком, в котором клиент подписывается на наборы данных. Первая подписка, которую делает клиент, имеет наивысший приоритет, вторая подписка - следующая по величине и так далее.
Поскольку поле слияния содержит состояние клиента, оно может отправлять минимальный объем данных, чтобы каждый клиент был в актуальном состоянии, независимо от того, какая функция публикации его передает.
Что происходит при обновлении
Итак, теперь мы подготовили почву для вашего сценария.
У нас 1000 подключенных клиентов. Каждый подписан на один и тот же живой запрос Mongo ( Somestuff.find({})
). Поскольку запрос одинаков для каждого клиента, драйвер выполняет только один оперативный запрос. Есть 1000 активных ящиков слияния. И каждый клиент опубликуем функция зарегистрирован ли added
, changed
и
removed
на этой живой запрос , который питается в одном из слияния коробки. Больше ничего не связано с блоками слияния.
Сначала водитель Mongo. Когда один из клиентов вставляет новый документ Somestuff
, он запускает повторное вычисление. Драйвер Mongo повторно выполняет запрос для всех документов в Somestuff
, сравнивает результат с предыдущим результатом в памяти, обнаруживает, что есть один новый документ, и вызывает каждый из 1000 зарегистрированных insert
обратных вызовов.
Затем функции публикации. Здесь происходит очень немногое: каждый из 1000 insert
обратных вызовов отправляет данные в поле слияния путем вызова added
.
Наконец, каждый блок слияния проверяет эти новые атрибуты на их копию в памяти кеша своего клиента. В каждом случае он обнаруживает, что значений еще нет на клиенте и не затеняет существующее значение. Таким образом, поле слияния выдает DATA
сообщение DDP при соединении SockJS со своим клиентом и обновляет свою копию в памяти на стороне сервера.
Общая стоимость ЦП - это стоимость выполнения одного запроса Mongo плюс стоимость 1000 блоков слияния, которые проверяют состояние своих клиентов и создают новые полезные данные сообщения DDP. Единственные данные, которые передаются по сети, - это один объект JSON, отправленный каждому из 1000 клиентов, соответствующий новому документу в базе данных, плюс одно сообщение RPC на сервер от клиента, который сделал исходную вставку.
Оптимизация
Вот что мы точно запланировали.
Более эффективный драйвер Mongo. Мы
оптимизировали драйвер
в 0.5.1, чтобы запускать только одного наблюдателя для каждого отдельного запроса.
Не каждое изменение БД должно вызывать повторное вычисление запроса. Мы можем внести некоторые автоматические улучшения, но лучший подход - это API, который позволяет разработчику указывать, какие запросы необходимо повторно запустить. Например, разработчику очевидно, что вставка сообщения в одну комнату чата не должна приводить к недействительности текущего запроса сообщений во второй комнате.
Драйвер Mongo, функция публикации и поле слияния не должны запускаться в одном процессе или даже на одном компьютере. Некоторые приложения выполняют сложные запросы в реальном времени и требуют больше ЦП для наблюдения за базой данных. У других есть только несколько отдельных запросов (представьте движок блога), но, возможно, много подключенных клиентов - им требуется больше ЦП для блоков слияния. Разделение этих компонентов позволит нам масштабировать каждую деталь независимо.
Многие базы данных поддерживают триггеры, которые срабатывают при обновлении строки и предоставляют старые и новые строки. Благодаря этой функции драйвер базы данных может зарегистрировать триггер вместо запроса изменений.