Как масштабировать приложение с состоянием NodeJS

в настоящее время я работаю над веб-MMORPG игрой и хотел бы настроить стратегию автоматического масштабирования на основе Docker и DigitalOcean капель.

однако мне интересно, как мне это удалось:

мой игровой сервер должен быть разбит на разные контейнеры Docker но каждый экземпляр игрового сервера должен действовать так, как если бы это был только один гигантский игровой сервер. Это означает, что каждая модификация, происходящая в одном (перемещение персонажа), должна также быть зеркальным в каждом другом игровом сервере.

Я пытаюсь получить эту работу (по крайней мере, концептуально), но не могу найти способ, чтобы синхронизировать все мои экземпляры. Должен ли я использовать мастер только для трансляции событий или есть альтернатива?

Мне было интересно то же самое о моей базе данных MySQL: так как каждый игровой сервер должен был бы читать/писать из/в БД, как бы я сделал его масштаб должным образом, поскольку игра становится все больше и больше? Лучшее решение, которое я мог придумать был сохранить базу данных на одном сервере, который будет очень мощный.

Я понимаю, что это может быть легко, если все игровые серверы не должны были "делиться" своим состоянием, но это в первую очередь считается, чтобы я мог быстро масштабироваться в случае внезапного всплеска активности.

(будут разные "глобальные" игровые серверы, такие как A, B, C... но каждый из этих глобальных игровых серверов должен быть, за кулисами, состоит из 1-х контейнеров docker под управлением "реальный" игровой сервер, так что "глобальный" игровой сервер-это только концепция)

3 ответов


проблема, которую вы заявляете, слишком общая, и трудно дать конкретный ответ. Однако позвольте мне быть безрассудным и дать вам несколько советов по масштабированию общего назначения:

  • удалить счетчики из базы данных. Вместо первичных ключей, которые являются автоматически увеличенными идентификаторами, попробуйте назначить случайные UUID.

  • изменить данные, которые должны быть проверены против центральной точки с помощью данных, которые являются автономными. Например, для аутентификации вместо Учетные данные пользователя в БД используйте веб-маркеры JSON, которые могут быть проверены любым хостом.

  • используйте методы как последовательный хэшировать для того чтобы сбалансировать нагрузку без потребности балансеров нагрузки. Конечно, используйте функции хэширования, которые хорошо распределяются, чтобы избежать / минимизировать столкновения.

вышеуказанные советы в основном касаются изменения дизайна для перехода от stateful к апатриду в максимально возможных аспектах. Если вам все равно нужно предоставить stateful части, попробуйте угадать, какие сущности будут иметь больше шансов обмениваться данными с состоянием и выделять их в том же (или почти сервере). Например, если в вашей игре есть города, попробуйте выделить на одном сервере пользователей, которые находятся в одном городе, поскольку они больше готовы взаимодействовать между собой (и обмениваться данными с состоянием), чем пользователи, которые находятся в разных городах.

конечно, если город слишком большой и он очень переполнен, вам, вероятно, нужно будет разделить город на более сервера, чтобы избежать перегрузки сервера.


ваш вопрос слишком широк и общая проблема масштабирования, как упоминали другие. Было бы полезно, если бы вы более четко изложили свои системные требования.

Если это должно быть в режиме реального времени, то вы можете выбрать Redis в качестве основной БД, но тогда вам понадобятся рабы (для репликации), и вы не сможете масштабироваться автоматически, когда вы идете*, так как Redis не поддерживает это. Я предполагаю, что это не хороший вариант, когда вы работаете с играми (внезапные всплески вероятно)

*кажется, есть некоторые управляемые решения, вам нужно проверить их

Если это может быть близко к реальному времени, использование Apache Kafka может оказаться полезным.

есть также масштабируемая БД, которая имеет все, что вам нужно называется (Я вкладчик, ура!) но вам нужно запустить тесты, чтобы увидеть, соответствует ли он вашим требованиям задержки.

в целом, идет с очень мощный сервер-плохой выбор, так как есть потолок, и вам будет стоить больше масштабироваться по вертикали.


существует большое преимущество в горизонтальном масштабировании такого приложения. Я постараюсь записать несколько идей.

Вариант 1 (с учетом состояния):

при планировании приложений с состоянием вам нужно позаботиться о синхронизации состояния (через PubSub, сетевое вещание или что-то еще) и знать, что каждая синхронизация займет время (когда не блокируется каждая операция). Если это нормально для вас, давайте продолжим.

давайте у вас есть 80k операций в секунду на всем кластере. Это означает, что каждый процесс должен синхронизировать изменения состояния 80k в секунду. Это будет узким местом. Обработка изменений 80k в секунду-это большая проблема для узла.приложение js (потому что оно однопоточное и, следовательно, блокирует).

в конце вам нужно будет точно указать максимальное количество изменений, которые вы хотите синхронизировать и выполнить некоторые тесты с различными языками программирования. Этот накладные расходы на синхронизацию должны быть добавлены к общей рабочей нагрузке приложения. Было бы полезно использовать какой-то многопоточный язык, такой как C, Java/Scala или Go.

Вариант 2 (stateful с маршрутизацией):*

в некоторых случаях возможно реализовать другой вид масштабирования. Когда, например, ваше приложение можно разбить на области карты, вы можете начать с одной репликации приложения, которая содержит полную карту, и когда она масштабируется, она делится картой пропорционально. Вам нужно будет реализовать некоторую маршрутизацию между серверами приложений, например, чтобы изменить состояние в городе a world B => call server xyz. Это может быть сделано автоматически, но масштабирование будет проблемой.

Это решение требует большей осторожности и знаний о приложении и не является таким отказоустойчивым, как вариант 1, но может масштабироваться бесконечно.

Вариант 3 (без гражданства):

переместить государства к какому-то другому приложению и решить проблему в другом месте (например, Redis, Etcd,...)