Подтвердить что ты не робот

Параметры взаимодействия между дочерними процессами в кластере node.js

Итак, я сейчас работаю над приложением node.js игрового сервера, и здесь я немного набросился на стену. Моя проблема в том, что я использую socket.io для приема входящих соединений от игровых клиентов. Эти клиенты могут быть подключены к одной из нескольких зон или областей игрового мира.

Ниже представлена ​​базовая архитектура. Мастер-процесс создает дочерний процесс для каждой зоны игры, которая запускает процесс Zone Manager; процесс, посвященный сохранению данных зоны (3d-модели, позиции игроков/сущностей и т.д.). Затем мастер-процесс создает несколько "потоков связи" для каждого созданного им диспетчера зон. Эти потоки создают экземпляр socket.io и прослушивают порт для этой зоны (несколько потоков прослушивают один порт). Эти потоки будут обрабатывать большую часть логики игры в своем собственном процессе, а также связываться с базой данных, поддерживающей игровой сервер. Единственная проблема заключается в том, что в некоторых случаях им может потребоваться связаться с диспетчером зоны для получения информации о Зоне, игроках и т.д.

Architecture

В качестве примера: игрок хочет покупать/продавать/торговать с персонажем без игрока (NPC) в Зоне. Поток связи зоны должен запрашивать поток Zone Manager, если игрок достаточно близко к NPC, чтобы совершить сделку, прежде чем она разрешит сделку.

Проблема, с которой я столкнулась, заключается в том, что я планировал использовать функциональность кластера node.js и использовать методы send() и on() процессов для обработки передаваемых сообщений назад и вперед. Это было бы хорошо, за исключением одного предостережения, с которым я столкнулся. Поскольку все дочерние процессы, выделенные с помощью cluster.fork(), могут взаимодействовать только с "ведущим" процессом. Корневой процесс node.js становится узким местом для всей коммуникации. Я провел несколько тестов в своей системе, используя script, который буквально просто отскакивал сообщение назад и вперед, используя кластерную межпроцессную связь (IPC) и отслеживал, сколько реле в секунду выполняется. Похоже, что в конце концов node пропустит около 20k в секунду с точки зрения количества IPC, которое он может передать. Это число было последовательным как на четырехъядерном ноутбуке Phenom II 1.8ghz, так и на 8-ядерном настольном компьютере FX-8350 4.0ghz.

Теперь это звучит довольно прилично высоко, за исключением того, что это в основном означает, что независимо от того, сколько зон или коммуникационных потоков есть, все IPC все еще сталкиваются с узким прохождением через один процесс, который действует как "реле" для всего приложения. Это означает, что, хотя кажется, что каждый отдельный поток может передавать > 20 тыс. IPC в секунду, все приложение в целом никогда не будет передавать больше, даже если оно было в какой-то безумной 32-я системной системе, поскольку все сообщения проходят через один поток.

Итак, проблема у меня. Теперь дилемма. Я много читал о различных других вариантах и ​​читал, как 20 различных вопросов здесь, о стеке по этой теме, и я видел пару вещей, появляющихся регулярно:

Redis: На самом деле я на самом деле запускаю Redis на моем сервере и использую его в качестве хранилища socket.io, так что socket.io в нескольких потоках может совместно использовать данные соединения, чтобы пользователь мог подключиться к любому из N номеров потоков socket.io для их Zone, поэтому сервер может сортировать автоматически загружать баланс входящих соединений.

Меня беспокоит, что он проходит через сетевой стек. Вряд ли идеально подходит для связи между несколькими процессами на одном сервере. Я чувствую, что латентность будет серьезной проблемой в долгосрочной перспективе.

0MQ (zeromq/zmq): Я никогда раньше не использовал этот материал для чего-либо, но в последнее время я немного об этом слышал. Основываясь на чтении, которое я сделал, я нашел множество примеров использования его людьми с сокетами TCP, но о людях, использующих его для IPC, не так много шума. Я надеялся, что, возможно, кто-то здесь работал с 0MQ для IPC раньше (возможно, даже в node.js?) И мог бы осветить этот параметр для меня.

dnode: Снова я никогда не использовал это, но из того, что я видел, он выглядит как другой вариант, который предназначен для работы через TCP, что означает, что сетевой стек снова встает на пути.

node-udpcomm:Кто-то связал это в другом вопросе здесь (чего я, похоже, не могу найти, к сожалению). Я даже не слышал об этом, и это похоже на очень маленькое решение, которое открывается и слушает UDP-соединения. Хотя это, вероятно, все же будет быстрее, чем параметры TCP, у нас все еще есть сетевой стек правильно? Мне определенно нравится около мили за пределами моей "зоны программистов", как здесь, и в области архитектуры/компьютерной архитектуры, о которой я мало что знаю о lol

В любом случае суть в том, что я полностью застрял здесь и понятия не имею, какой лучший вариант для IPC в этом сценарии. В настоящий момент я предполагаю, что 0MQ является наилучшим вариантом для тех, которые я перечислил выше, так как это единственный вариант, который предлагает вариант IPC для протокола связи, который, как я полагаю, означает его использование сокета UNIX или что-то, что не проходя через сетевой стек, но я не могу подтвердить это или что-то еще.

Наверное, я просто надеюсь, что некоторые люди здесь могут знать достаточно, чтобы указать мне в правильном направлении или сказать, что я уже там. Проект, над которым я работаю, - это многопользовательский игровой сервер, предназначенный для работы "из коробки" с многопользовательским игровым клиентом, который обеспечивает их трехмерную графику/расчеты с помощью Three.js. Клиент и сервер будут открытыми для всех, как только я получу их всех, работающих на мое удовлетворение, и я хочу убедиться, что архитектура настолько масштабируема, насколько это возможно, чтобы люди не строили игру на этом, а затем переходили в масштаб и в конечном итоге ударить по стене.

В любом случае спасибо за ваше время, если вы действительно прочитали все это:)

4b9b3361

Ответ 1

Я думаю, что 0MQ будет очень хорошим выбором, но я признаю, что не знаю других: D

Для 0MQ прозрачно, какой транспорт вы решаете использовать, вызовы библиотеки одинаковы. Это просто выбор конкретной конечной точки (и, следовательно, транспорта) во время вызова zmq_bind и zmq_connect в начале. Есть в основном 4 пути, которые вы можете решить:

  • "inproc://<id>" - конечная точка связи в процессе между потоками через память
  • "ipc://<filepath>" - системная конечная точка связи между процессами
  • "tcp://<ip-address>" - clear
  • "pgm://..." или "epgm://..." - конечная точка для Pragmatic Reliable Multicast

Итак, просто, чем выше в списке, тем быстрее он и меньше проблем с учетом латентности и надежности, с которыми вам приходится сталкиваться. Поэтому вы должны стараться держаться как можно выше. Естественно, поскольку ваши компоненты являются процессами, вы должны пойти с транспортом IPC. Если вам потом нужно что-то изменить, вы можете просто изменить определение конечной точки, и вы в порядке.

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

  • Менеджер: REP socket, Threads: REQ socket; или
  • Менеджер: ROUTER, Threads: REQ или DEALER

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

Но для подробного описания того, что означают все эти типы сокетов и шаблонов, определенно выходит за рамки этой публикации, но вы можете и должны больше узнать об этом в Руководство ZeroMQ. Там вы можете не только узнать обо всех типах сокетов, но и о различных способах подключения ваших компонентов и позволить им разговаривать друг с другом. Тот, который я упомянул, просто очень простой. Как только вы поймете, вы можете создавать произвольные иерархии. Это как LEGO; -)

Надеюсь, это помогло немного, ура!