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

Открытие акков Акки в кластере

В последнее время я пытался замалчивать концепцию Акка и актерских систем. В то время как у меня есть довольно хорошее понимание основ Akka, теперь Im все еще борется с некоторыми вещами, когда дело доходит до кластеров и удаленных участников.

Попытайтесь проиллюстрировать проблему, используя пример чата WebSocket, который поставляется с Play Framework 2.0: там есть актер, который содержит WebSockets и который хранит список текущих подключенных пользователей. Актеры в основном представляют чат-комнату как технически, так и логично. Это работает отлично, если только один чат работает на одном сервере.

Теперь я пытаюсь понять, как этот пример должен быть расширен, когда мы говорим о многих динамических чатах (в любой момент можно открывать/закрывать новые комнаты), работающие на кластере серверов (с единственными узлами, которые добавляется или удаляется в соответствии с текущим спросом). В таком случае пользователь A может подключиться к серверу 1, когда пользователь B подключается к серверу 2. Оба могут разговаривать в одной комнате чата. На каждом сервере все равно будет актер (для каждой комнаты чата?), Который содержит экземпляры WebSocket для приема и публикации событий (сообщений) для нужных пользователей. Но логически на одном сервере 1 или сервере 2 должен присутствовать только один участник чата, в котором содержится список подключенных пользователей (или подобных задач).

Как вы собираетесь достичь этого, желательно в "чистой акке" и без добавления дополнительной системы обмена сообщениями, такой как ZeroMQ или RabbitMQ?

Вот что я до сих пор придумал, пожалуйста, дайте мне знать, имеет ли это смысл:

  • Пользователь A подключается к серверу 1 и выделяется актер, который хранит его WebSocket.
  • Актер проверяет (используя Router? EventBus? Что-то еще?) существует ли "чат-актер" для активного чата на любом из подключенных узлов кластера. Так как он этого не сделает, он попросит создать новый чат-актер как-то и отправит и получит будущие сообщения в чате от этого актера.
  • Пользователь B подключается к серверу 2, а актер также выделяется для своего WebSocket.
  • Он также проверяет, существует ли какой-либо объект для запрашиваемой комнаты чата и находит его на сервере 1.
  • Актер чата на сервере 1 теперь выступает в качестве концентратора для данного чата, отправляет сообщения всем "подключенным" участникам чата и распределяет входящие.

Если сервер 2 идет вниз, актер чата должен быть повторно создан на/перемещен на сервер 2 каким-то образом, хотя сейчас это не моя основная проблема. Мне больше всего интересно узнать, как это динамическое открытие актеров распространилось по различным, в основном независимым машинам, с помощью инструментария Akkas.

Я смотрел документацию Аккаса уже довольно давно, так что, возможно, я пропустил здесь очевидное. Если да, пожалуйста, просветите меня: -)

4b9b3361

Ответ 1

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

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

На сервере присутствуют следующие участники:

  • WebsocketSession: он поддерживает соединение с одним пользователем и обрабатывает запросы пользователя и пересылает сообщения из системы.
  • ChatroomManager: это центральный вещатель, который развертывается на каждом экземпляре сервера. Если пользователь хочет отправить сообщение в чат, WebSocketSession-Actor отправляет всю информацию в ChatroomManager-Actor, который затем передает это сообщение всем членам чата.

Итак, вот моя процедура:

  • Пользователь A подключается к серверу 1, который выделяет новую WebsocketSession. Этот актер вставляет абсолютный путь этому актеру в redis.
  • Пользователь A присоединяется к комнате чата X, которая также вставляет свой абсолютный путь (я использую это как уникальный идентификатор сеанса пользователя) в redis (в каждом чате есть набор "соединений" )
  • Пользователь B подключается к серверу 2 → redis
  • Пользователь B присоединяется к чату X → redis
  • Пользователь B отправляет сообщение в комнату заседаний X следующим образом: пользователь B отправляет свое сообщение через Websocket своему сеансовому актеру, который (после некоторых проверок) отправляет сообщение -актору ChatroomManager. Этот актер фактически извлекает список пользователей в чате из redis (абсолютные пути, используемые с akka actorFor -методом), а затем отправляет сообщение каждому участнику-актеру. Затем эти участники сеанса записывают в свои веб-сайты.

В каждом игроке ChatroomManager я выполняю кеширование ActorRef, которое давало дополнительную скорость. Я думаю, что это отличается от вашего подхода, тем более что эти ChatroomManagers обрабатывают запросы для всех чатов. Но наличие одного актера для одного чата - это единственная точка неудачи, которую я хотел избежать. Кроме того, это вызовет гораздо больше сообщений, например:

  • Пользователь A и пользователь B находятся на сервере 1.
  • Chatroom X находится на сервере 2.

Если пользователь A хочет поговорить с пользователем B, им обоим придется общаться через чат-актер на сервере 1.

Кроме того, я использовал функции akka, такие как (round-robin) -routers, чтобы создавать несколько экземпляров агента ChatroomManager в каждой системе для обработки многих запросов.

Я потратил несколько дней на настройку всей удаленной инфраструктуры akka в сочетании с сериализацией и redis. Но теперь я могу создать любое количество экземпляров серверного приложения, которые используют redis для обмена там ActorRef (сериализованы как абсолютные пути с ip + портом).

Это может помочь вам немного дальше, и я открыт для новых вопросов (пожалуйста, не о моем английском;).

Ответ 2

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

Ядро вашей проблемы заключается в том, что чат-комната представлена ​​одним актером, работающим на одной машине, или даже если такая комната существует вообще. Хитрость заключается в маршрутизации запросов, связанных с данным чатом, с использованием идентификатора, например, имени чата. Вычислите хэш имени и, в зависимости от числа, выберите один из своих n полей. node будет знать о текущих чатах и ​​может безопасно найти или создать для вас подходящего актера чата.

Вы можете взглянуть на следующие сообщения в блоге, посвященные кластеризации и масштабированию в Akka:

http://blog.softmemes.com/2012/06/16/clustered-akka-building-akka-2-2-today-part-1/

http://blog.softmemes.com/2012/06/16/clustered-akka-building-akka-2-2-today-part-2/

Ответ 3

Я бы использовал Zookeeper + Norbert, чтобы узнать, какие хосты идут вверх и вниз:

http://www.ibm.com/developerworks/library/j-zookeeper/

Теперь каждый node в моей ферме серверов чата может знать все хосты в логическом кластере. Они получат обратный вызов, когда node переходит в автономный режим (или выходит в сеть). Любой node теперь может сохранять отсортированный список текущих членов кластера, хешировать идентификатор чата и мод по размеру списка, чтобы получить индекс в списке, который представляет собой node, который должен размещать какой-либо конкретный раздел в чате. Мы можем добавить 1 и перефразировать, чтобы выбрать второй индекс (требуется цикл, пока вы не получите новый индекс), чтобы вычислить второй хост для хранения второй копии чата для избыточности. На каждом из двух хостов чата - участник чата, который просто передает все сообщения чата каждому актеру Websocket, который является членом чата.

Теперь мы можем отправлять чат-сообщения через обоих активных участников чата с обычным маршрутизатором Akka. Клиент просто отправляет сообщение один раз, и маршрутизатор будет выполнять хэш-моды и отправлять их двум участникам удаленных чатов. Я бы использовал алгоритм twitter snowflake для генерации уникальных 64-битных идентификаторов для отправляемых сообщений. См. Алгоритм в методе nextId() кода по следующей ссылке. DatacenterId и workerId могут быть установлены с использованием свойств norbert, чтобы гарантировать, что на разных серверах не создаются встречные идентификаторы:

https://github.com/twitter/snowflake/blob/master/src/main/scala/com/twitter/service/snowflake/IdWorker.scala

Теперь две копии каждого сообщения будут отправляться в каждую конечную точку клиента через каждого из двух активных участников чата. У каждого участника-клиента Websocket я бы не размаскивал идентификаторы снежинок, чтобы узнать номер datacenterId + workerId, отправляющий сообщение, и отслеживать самый высокий номер сообщения чата, видимый с каждого хоста в кластере. Тогда я проигнорировал бы любые сообщения, которые не выше, чем то, что уже было видно на данном клиенте для данного узла-отправителя. Это позволит дедуплицировать пару сообщений, поступающих через двух активных участников чата.

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

Затем нам нужно иметь дело с удалением узлов из кластера или добавлением обратно в кластер. Мы получим обратный вызов norbert в каждом node, чтобы уведомить нас об изменениях членства в кластере. В этом обратном вызове мы можем отправить сообщение akka через настраиваемый маршрутизатор с указанием нового списка участников и текущего имени хоста. Пользовательский маршрутизатор на текущем узле увидит это сообщение и обновит его состояние, чтобы узнать о новом членстве в кластере, чтобы вычислить новую пару узлов для отправки любого заданного трафика в чате. Это подтверждение нового членства в кластере будет отправлено маршрутизатором на все узлы, чтобы каждый сервер мог отслеживать, когда все серверы догнали изменения членства и теперь правильно отправляют сообщения.

Сохраняющийся чат может оставаться активным после изменения членства. В этом случае все маршрутизаторы на всех узлах будут продолжать отправлять на него как обычно, но также будут отправлять сообщение спекулятивно новому хозяину чата. Этот второй чат может еще не подняться, но это не проблема, так как сообщения будут проходить через оставшегося в живых. Если оставшийся в живых чат больше не будет активен после изменения членства, все маршрутизаторы на всех хостах сначала отправят на три хоста; выживания и двух новых узлов. Механизм часов смерти akka можно использовать так, чтобы все узлы могли в конечном итоге увидеть выживание оставшегося чата, чтобы вернуться к маршрутизации трафика чата через два хоста.

Затем нам нужно перенести чат из оставшегося сервера на один или два новых хоста в зависимости от обстоятельств. В какой-то момент увлекательный чат-актер получит сообщение о новом членстве в кластере. Он начнется с отправки копии членства в чате для новых узлов. Это сообщение создаст новую копию участника чата с правильным членством на новых узлах. Если выживание уже не является одним из двух узлов, которые должны удерживать чат, он переходит в режим снятия с эксплуатации. В режиме снятия с охраны он будет перенаправлять только сообщения на новые первичные и вторичные узлы, не являющиеся членами чата. Передача сообщений Akka идеально подходит для этого.

Разлучающий чат будет прослушивать сообщения подтверждения членства в кластере norbert от каждого node. В конце концов он увидит, что все узлы в кластере признали новое членство в кластере. Затем он знает, что больше не будет получать никаких дальнейших сообщений для пересылки. Затем он может убить себя. Акка-горячее резервирование идеально подходит для реализации режима снятия с эксплуатации.

До сих пор так хорошо; у нас есть отказоустойчивая система обмена сообщениями, которая не потеряет сообщения для сбоя node. В момент изменения членства в кластере мы получим всплеск трафика внутри сети, чтобы скопировать чаты в новые узлы. У нас также есть остаточный шквал интранодной пересылки сообщения узлам, пока все серверы не догнали, в какие чаты переместили два сервера. Если мы хотим расширить систему, мы можем подождать до низкой точки в пользовательском трафике и просто включить новый node. Чаты будут автоматически перераспределяться по новым узлам.

Вышеприведенное описание основано на чтении следующей статьи и переводе ее на понятия akka:

https://www.dropbox.com/s/iihpq9bjcfver07/VLDB-Paper.pdf