Короткий ответ: по сети отправляются только новые данные. Вот как это работает.
Есть три важных части сервера 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, функция публикации и поле слияния не должны запускаться в одном процессе или даже на одном компьютере. Некоторые приложения выполняют сложные запросы в реальном времени и требуют больше ЦП для наблюдения за базой данных. У других есть только несколько отдельных запросов (представьте движок блога), но, возможно, много подключенных клиентов - им требуется больше ЦП для блоков слияния. Разделение этих компонентов позволит нам масштабировать каждую деталь независимо.
Многие базы данных поддерживают триггеры, которые срабатывают при обновлении строки и предоставляют старые и новые строки. Благодаря этой функции драйвер базы данных может зарегистрировать триггер вместо запроса изменений.
skip
,$near
и$where
содержащие запросы) , который является гораздо более эффективным в CPU нагрузке, пропускной способности сети и позволяет масштабирование приложения серверы.По моему опыту, с версией 0.7.0.1 использование многих клиентов с одновременным обменом огромной коллекцией в Meteor практически не работает. Я постараюсь объяснить почему.
Как описано в вышеупомянутом сообщении, а также на https://github.com/meteor/meteor/issues/1821 , сервер Meteor должен хранить копию опубликованных данных для каждого клиента в поле слияния . Это то, что позволяет происходить магии Meteor, но также приводит к тому, что любые большие общие базы данных постоянно хранятся в памяти процесса узла. Даже при использовании возможной оптимизации для статических коллекций, такой как в ( Есть ли способ сказать метеору, что коллекция статична (никогда не изменится)? ), Мы столкнулись с огромной проблемой с использованием ЦП и памяти процессом Node.
В нашем случае мы публиковали набор из 15 тысяч документов для каждого клиента, который был полностью статичным. Проблема в том, что копирование этих документов в клиентское поле слияния (в памяти) при подключении в основном доводило процесс Node до 100% ЦП почти на секунду и приводило к большому дополнительному использованию памяти. Это по своей сути немасштабируемо, потому что любой подключающийся клиент поставит сервер на колени (а одновременные соединения будут блокировать друг друга), а использование памяти будет линейно расти в зависимости от количества клиентов. В нашем случае каждый клиент вызвал дополнительные ~ 60 МБ использования памяти, хотя переданные необработанные данные составляли всего около 5 МБ.
В нашем случае, поскольку коллекция была статической, мы решили эту проблему, отправив все документы в виде
.json
файла, который был сжат с помощью nginx, и загрузив их в анонимную коллекцию, что привело к передаче данных только ~ 1 МБ без дополнительного процессора. или память в процессе узла и гораздо более быстрое время загрузки. Все операции с этой коллекцией были выполнены с использованием_id
s из гораздо меньших по размеру публикаций на сервере, что позволило сохранить большую часть преимуществ Meteor. Это позволило масштабировать приложение для большего числа клиентов. Кроме того, поскольку наше приложение в основном предназначено только для чтения, мы дополнительно улучшили масштабируемость, запустив несколько экземпляров Meteor за nginx с балансировкой нагрузки (хотя и с одним Mongo), поскольку каждый экземпляр Node является однопоточным.Однако проблема совместного использования больших записываемых коллекций между несколькими клиентами - это инженерная проблема, которую необходимо решить Meteor. Вероятно, есть лучший способ, чем хранить копию всего для каждого клиента, но это требует серьезного размышления как проблема распределенных систем. Текущие проблемы с массовым использованием ЦП и памяти просто не масштабируются.
источник
Эксперимент, который вы можете использовать, чтобы ответить на этот вопрос:
meteor create --example todos
Советы по использованию WKI можно найти в этом статье . Это немного устарело, но в основном все еще актуально, особенно для этого вопроса.
источник
Этому все еще год, и поэтому я думаю, что знания были до "Meteor 1.0", так что, возможно, все снова изменилось? Я все еще занимаюсь этим. http://meteorhacks.com/does-meteor-scale.html приводит к вопросу «Как масштабировать Метеор?» статья http://meteorhacks.com/how-to-scale-meteor.html
источник