Концепция Message Passing. Агенты и актёры
Данная статься начитает серию публикаций о технологиях, которые мы используем и изучаем для разработки нашего сервиса мониторинга веб сайтов HostTracker. Надеемся, наш опыт окажется полезным.Message passing является одной из популярных концепций параллельного программирования. Она часто используется при создании сложных распределенных систем с высокой степенью параллелизма. Реализация этой концепции представлена в языках программирования в качестве актёров (actor) или агентов (agent).
Расспределенные агенты HostTracker. Быстрая проверка с http://updownchecker.com
Особенности message passing: • Решение состоит из изолированных компонент, которые работают параллельно (в параллельных потоках из пула потоков). Взаимодействие между компонентами идет через обмен сообщениями по определенному протоколу. Сетевые компоненты могут использовать TCP, UDP, HTTP, и т.д. Локальные взаимодействуют через протокол, определенный конкретный языком его реализации или библиотекой.• Компонент определяет логику обработки входных сообщений. Последние попадают в очередь (queue) и последовательно достаются из нее для обработки.• Компонент может быть владельцем некоторых ресурсов и быть их провайдером для других компонент. Ресурсом могут быть: данные в определенном формате в оперативной памяти, аппаратно-программный ресурс или их комбинация.• Компонент имеет определенное состояние, которое может инкапсулировать ресурс (из предыдущего пункта), или, как в случае машины состояний (state machine), может быть выражен в виде определенного алгоритма обработки сообщений, который переводит его в другое состояние.• Интерфейс между компонентами: — postSync, postAsync — послать сообщение компоненте синхронно или асинхронно.— receiveSync, receiveAsync — получить сообщение от компонента синхронно или асинхронно. Асинхронное ожидание состоит в том, что поток как ресурс возвращается системе и может быть использован для другой работы. В этом случае система регистрирует функцию обратного вызова (callback) на определенное событие.— tryReceive функции — аналогичные вышеперечисленным, но имеющим определенную задержку для получения данных.• Концепция тесно связана с понятием асинхронного исполнения.Асинхронное и параллельное исполнение Асинхронное исполнение значит, что поток не ожидает результат исполнения определенной операции, используя при этом ресурсы операционной системы. Вместо этого, поток возвращается в пул и может быть использован для других операций. В ОС при этом регистрируется callback-функция, которая принимает результат асинхронной операции. Эта функция запускается по факту получения результата (в ОС Windows через механизм портов завершения IO Completion ports, в Nix системах через другие механизмы ядра).Реализации концепции message passing Реализация Язык Тип взаимодействия Особенности Разработчик Erlang processes Erlang локальный, сетевой Первая реализация для телекоммуникационного оборудования. Может использоваться для сетевого взаимодействия. Определен только метод отсылки сообщений вида Pid! message. Ericson corp. MailboxProcessor F# локальный Использует асинхронные вычисления. Дополнительно появляется понятие канала ответа — AsyncReplyChannel, который позволяет получат ответ синхронно и асинхронно. Microsoft research TPL dataflow .NET языки локальный Набор примитивов — блоков, которые реализуют приемники и источники данных. Microsoft Scala actors Scala (Java machine) локальный, сетевой Реализация напоминает классическую — Erlang processes. EPFL, Typesafe Inc. NodeJs JavaScript сетевой Event-driven подход к разработке приложения: декларативное определение асинхронных операций и коллбеков, очередь вызова обработчика событий. Внешний интерфейс определяется разработчиком приложения. Joyent Inc. MPI: MPICH, HP-MPI, SGI-MPI, WMPI, C, C++, Java, Fortran сетевой Специфицированный интерфейс взаимодействия узлов сети. Единый исполняемый код для всех узлов с выбором ветки исполнения по идентификатору процесса. Microsoft, HP, SGI, Argonne National Laboratory, others Разные языки программирования по-разному поддерживают эту концепцию. Так, JavaScript (платформа NodeJs) является примером Event-Driven языка (язык, который имеет встроенную поддержку работы с асинхронными вычислениями). Типичная программа на NodeJs — декларативная регистрация callback функций на ответы событий IO. В начале ее исполнения создается очередь операций, в которой и срабатывают callback-функции. То есть сама программа является однопоточной, но асинхронной. То есть сама программа является однопотоковой, но асинхронной. В данном случае виртуальная машина NodeJs (основана на Chrome v8 JavaScript runtime) является примером реализации message passing и удовлетворяет все вышеперечисленные условия, если реализует определенный сетевой интерфейс взаимодействия с другими компонентами распределенного применения (например, REST сервис).
В некоторых языках (например, F# — async монада, С# — async, await) существует понятие асинхронного вычисления — части кода, определенной разработчиком, которая содержит как синхронные, так и асинхронные операции. Также, существует возможность запустить вычисления в параллельном потоке, отменить вычисления, сделать синхронный запуск.
Примеры реализации концепции Message Passing Концепция message passing впервые была представлена в языке Erlang (функциональный язык с динамической типизацией), который был разработан компанией Ericson для специализированного телекоммуникационного оборудования. Процессы Erlang представляют собой компоненты, описанные ранее. Их создание и обмен данными между ними, в отличии от процессов ОС, не нуждается значительного временного ресурса. Современные функциональные языки, такие как Scala (Java платформа) и F# (.NET платформа) взяли реализацию message passing именно из Erlang. Scala-агенты могут существовать как в рамках одного процесса ОС, так и в разных процессах на разных компьютерах. Принципы работы с ними аналогичны принципам Erlang-процессов. F# предлагает несколько другой подход — MailboxProcessor. Также, свои особенности реализации message passing можно найти в библиотеке TPL Dataflow, разработанной для языков .NETTPL Dataflow — .NET (может быть использована в языках C#, VB, F#); Сетевое взаимодействие построено на принципах message passing. Здесь компонентом является программа на отдельной машине, ресурсами являются все аппаратные составляющие компьютера + отдельные функции, которые делегированы данному вычислительному узлу. Данные хранятся в памяти процесса приложения. Интерфейс взаимодействия с другими компонентами (узлами) определяется конкретной технологией, обычно созданной на Berkley sockets. Как пример такой технологии, можно привести MPI (Message passing interface). Его особенность состоит в том, что каждый узел обрабатывает один и тот же код, выбирая отдельную ветку для обработки с помощью условных операторов с использованием идентификатора потока. Нулевой поток является выделенным. Обмен данными по сети происходит через интерфейс, аналогичный вышеупомянутому, (концепция message passing) и использует идентификаторы потока для обозначения источника и/или приемника дынных. Другой пример — сервисы REST и SOAP, которые дают возможность разработчику самому определить сетевой интерфейс. И хотя, на первый взгляд, кажется, что таким образом определенный интерфейс не впишется в концепцию message passing, он является еквивалентом набору операций post (send) и receive. Так, в SOAP вызов сетевой функции происходит обращением по HTTP xml конверта (envelope), который включает информацию про функцию. Аналогично работают функции в REST — используются HTTP запросы с форматом данных, определяемым разработчиком.
В дальнейших публикациях будет детально рассмотрен MailboxProcessor F# (активно используется в системе HostTracker)и особенности работы с библиотекой TPL DataFlow.