Skip to content

Latest commit

 

History

History
108 lines (87 loc) · 13.4 KB

Distributed.RU.md

File metadata and controls

108 lines (87 loc) · 13.4 KB

Распределённые системы

Собранная система может быть развёрнута как локально, на одном хосте, так и на нескольких хостах, связанных сетью.

Естественно, никто нам не мешает создать несколько отдельных систем, каждая из которых развёртывается на своём хосте, а затем просто связать эти системы через механизм встроенных экторов. Однако такой вариант не обеспечивает сплошного описания и возможности статического анализа всей системы в целом.

Поэтому более интересным выглядит вариант, когда описанная единственным образом система распределяется между несколькими хостами. Для этого надо, во-первых, получить идентичные экземпляры StaticSystem на каждом из хостов, участвующих в кластерном распределении. Во-вторых, надо иметь единый дескриптор, описывающий распределение подсистем по хостам. В момент создания runtime- системы на каждом из хостов, дескриптор анализируется и создаются только те подсистемы, которые назначены этому хосту.

Для описания того, на каком хосте располагается какая подсистема, используется Deployment descriptor. Подсистемы идентифицируются путём от корня (List[String]), а хосты - адресом хоста и портом. Для целей развёртывания на данном хосте необходимо иметь список подсистем, которые должны быть созданы на хосте, а также обратные адреса подсистем, находящихся на других хостах.

В SynapseGrid взаимодейстие подсистем между собой осуществляется исключительно через вертикальные связи. Родительская система передаёт данные, поступившие на входной контакт подсистемы, вниз, актору, представляющему подсистему. Актор передаёт данные с выходных контактов вверх, родительской системе. При расположении подсистемы на другом хосте, ей необходимо иметь ссылку ActorRef на родительскую систему и переадресовывать выходные данные на указанный адрес.

Одной из проблем системы экторов является децентрализованное создание функционирующих экторов. Такой подход вступает в противоречие с подходом SynapseGrid, основанным на формировании централизованной модели всей системы, с последующим созданием связной runtime-системы. В Akka напротив, экторы сами создают дочерних экторов согласно собственным представлениям. Исходный механизм применения Akka в SynapseGrid, заключающийся в создании иерархии экторов, прямо соответствующей иерархии подсистем, страдает от того, что его затруднительно масштабировать и сделать распределённым. Более перспективным выглядит конструирование специализированной иерархии экторов, приспособленной к выполнению вычислений, заданных статической системой. В простейшем случае такая иерархия может быть одноуровневой, когда родительский эктор создаёт и связывает между собой дочерних экторов, соответствующих подсистемам. В более сложном случае иерархия делается двухуровневой с созданием слоя дополнительных маршрутизирующих экторов.

Специализированная иерархия экторов обеспечивает явную передачу сообщений между экторами.

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

-- Однако мы можем использовать ActorPath вместо ActorRef даже если соответствующая подсистема ещё не создана. После завершения первой фазы развёртывания всех подсистем с использованием ActorPath, мы можем перейти ко второй фазе, в течение которой ActorPath будут разрешены в конкретные ActorRef'ы. В принципе, можем пользоваться не ActorRef'ами, а ActorPath'ами. По-видимому, с тем же успехом. Возможно, с некоторым ухудшением производительности.

-- Второй альтернативой является запуск actor'ов, соответствующих системам, в начальном состоянии без связей с другими экторами. После создания всех экторов им рассылается "карта местности" с конкретными привязанными ActorRef'ами по адресам подсистем. (Можно было бы даже и SignalProcessor'ы рассылать вместе с картой местности, но это приведёт к существованию состояния вне эктора.) Здесь остаётся проблема - при перезапуске эктора он снова окажется в начальном состоянии, а значит, не будет знать о соседях.

Ещё одним вариантом является использование роутеров, которые создаются для каждого хоста. Эти роутеры будут подставляться вместо обычных экторов в тех случаях, когда подсистема, с которой осуществляется взаимодействие, будет находиться на другом хосте.

Вариант: создаём роутеры для каждой родительской подсистемы. Создаём снизу вверх всех экторов вместе с внутреними SignalProcessor'ами. Каждой подсистеме передаём ссылку на родительский роутер. Подсистемы не создают дочерних экторов. Экторы, соответствующие дочерним подсистемам, передаются извне в конструкторе. После создания всех рабочих экторов, остаётся только передать родительским роутерам фактические ссылки на родителей (и они выполнят become). В распределённом случае для подсистем, находящихся на другом хосте, подставляются роутеры, пересылающие данные на удалённый хост.

Итак, принимаем схему реализации:

  1. Создаём Deployment Descriptor. Он описывает размещение всех подсистем по хостам. В частности, по пути системы позволяет определить строку ActorPath (или actorSelection).

  2. По схеме системы создаём роутеры для каждой подсистемы. Имена роутеров определяются иерархическим именем подсистемы. А именно, совпадают с путём к подсистеме. При этом сами роутеры не образуют иерархию экторов, а находятся на одном уровне.

  3. Согласно Deployment Descriptor'у создаём подсистемы, находящиеся на данном хосте.

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

  5. Роутер может поддерживать перерегистрацию подсистем, что позволяет сделать горячую замену хостов/подсистем.

  6. Хост с момента создания и в дальнейшем регулярно регистрируется на всех других хостах (возможно, в дальнейшем для больших решений можно будет сделать другую стратегию). При создании роутеров/подсистем, родителем у них выступает непосредственно хост. Он отвечает за пересоздание подсистем в случае необходимости. Хост получает полный deployment descriptor.

Сериализация контактов

При создании распределённых систем необходимо обмениваться сигналами между разными JVM. Для этого сигналы должны быть сериализуемыми. Сами сигналы представляют собой case class'ы, поэтому сериализуемы априори. Однако важнейшей частью сигнала является контакт. А для него мы используем обычные классы и во всех алгоритмах проверка на равенство осуществляется по адресу объекта. Поэтому при десериализации мы должны получать экземпляр контакта, который уже объявлен где-то.

Для того, чтобы обеспечить десериализацию, планируется поступить так. Добавляем extension в Static System Builder, чтобы можно было особым образом помечать контакты, которые нуждаются в сериализации/десериализации. (В принципе, можно сделать реестр всех контактов).

Каждый сериализуемый контакт получает свой уникальный идентификатор в пределах StaticSystem верхнего уровня. При создании хоста все сериализуемые контакты добавляются в ActorSystemExtension. При сериализации для контакта находится его идентификатор, а при десериализации наоборот, по идентификатору находим соответствующий контакт.

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