Библиотека сайта rus-linux.net
Riak и Erlang/OTP
Глава 15 из книги "Архитектура приложений с открытым исходным кодом", том 1.
Оригинал: "Riak and Erlang/OTP", глава из книги "The Architecture of Open Source Applications"
Авторы: Francesco Cesarini, Andy Gross and Justin Sheehy
Перевод: Н.Ромоданов
Creative Commons: Перевод был сделан в соответствие с лицензией Creative Commons. С русским вариантом лицензии можно ознакомиться здесь.
15.4. Другие поведения рабочих процессов
С помощью тех же самых идей можно реализовывать и были реализованы много других видов поведений рабочих процессов.
15.4.1. Автоматы конечных состояний
Конечные автоматы (или машины с конечным числом состояний - FSM), реализованные в модуле поведения gen_fsm
, являются важнейшим компонентом реализации стеков протоколов, используемых в сетях связи (той проблемной области, для которой первоначально и был создан язык Erlang). Состояния определяются как функции обратного вызова, в названиях которых отражено то, что возвращается в кортеже, имеющем переменную следующего состояния State
, и то, каким образом обновляются данные цикла. Вы можете отправлять события для этих состояний как синхронно, так и асинхронно. Модуль функции обратного вызова конечного автомата также должен иметь возможность экспортировать стандартные функции обратного вызова, например, init
, terminate
и handle_info
.
Разумеется, конечные автоматы не предназначены исключительно для целей телекоммуникаций. В Riak, они используются в обработчиках запросов. Когда клиент выдает запрос, например, get
, put
или delete
, процесс, услышавший этот запрос, создаст процесс, реализующий соответствующее поведение gen_fsm
. Например, riak_kv_get_fsm
отвечающий за обработку запросов get
, извлекает данные и отправляет их клиентскому процессу. Процесс FSM будет проходить через различные состояния, поскольку в нем определено, из каких узлов запрашивать данные, как в эти узлы отправлять сообщения, и как в ответ принимать данные, ошибки или тайм-ауты.
15.4.2. Обработчики событий
Обработчики и менеджеры событий являются еще одним поведением, реализованным в библиотечном модуле gen_event
. Идея состоит в том, чтобы создать централизованное место, где принимаются события определенного вида. События могут передаваться синхронно и асинхронно с заранее определенным набором действий, выполняемых при их получении. Возможными реакциями на события может быть запись их в файл, посылка сообщения об аварии в виде SMS или сбор статистических данных. Каждое из этих действий определяется в отдельном модуле обратного вызова с его собственными данными цикла, которые сохраняются между вызовами. Для каждого конкретного менеджера событий можно добавлять, удалять или обновлять обработчики событий. Таким образом, на практике, в каждом менеджере событий может быть много модулей обратного вызова, а в различных менеджерах событий также может быть много различных экземпляров таких модулей обратного вызова. К числу обработчиков событий относятся процессы, получающие сигналы тревоги, отслеживающие данные в реальном времени, следящие за событиями, связанными с оборудованием, или просто регистрирующие данные в журнале.
Одно из применений в Riak поведения gen_event
является управление подписками в «кольце событий», т. е. изменений принадлежности узлам разделов или назначение разделов узлам в кластере Riak. Процессы в узле Riak могут регистрировать функцию в экземпляре событий riak_core_ring_events
, реализующем поведение gen_event
. Всякий раз, когда центральный процесс, управляющий кольцом событий в этом узле, изменяет запись о принадлежности, он создает событие, в результате которого в каждом из этих модулей обратного вызова будет вызвана зарегистрированная функция. Таким образом, можно достаточно просто организовать реагирование различных частей Riak на изменения в одной из самых центральных структур данных Riak, причем не добавляя сложности в сам механизм централизованного управления этой структурой.
Наиболее распространенные модели распараллеливания и взаимодействия процессов обрабатываются с помощью трех основных типов поведения, которые мы только что рассмотрели: gen_server
, gen_fsm
и gen_event
. Тем не менее, в больших системах с течением времени возникает необходимость в некоторых моделях, предназначенных для конкретного приложения, , т. е. нужно создавать новые виды поведений. В Riak есть одно такое поведение, riak_core_vnode
, в котором формализована реализация виртуальных узлов. Виртуальные узлы являются первичной абстракцией хранения данных в Riak, которая по запросам, управляемым с помощью конечных автоматов, предоставляет единый интерфейс хранилища данных вида «ключ - значение». Интерфейс модулей обратного вызова задается с помощью функции behavior_info/1
следующим образом:
behavior_info(callbacks) -> [{init,1}, {handle_command,3}, {handoff_starting,2}, {handoff_cancelled,1}, {handoff_finished,2}, {handle_handoff_command,3}, {handle_handoff_data,2}, {encode_handoff_item,2}, {is_empty,1}, {terminate,2}, {delete,1}];
В приведенном выше примере показана функция behavior_info/1
узла riak_core_vnode
. В списке кортежей {CallbackFunction, Arity}
определен контракт, которому должны следовать модули обратного вызова. Эти функции должны быть экспортированы в конкретные реализации виртуальных узлов, иначе компилятор выдаст предупреждение. Реализация своего собственного поведения OTP относительно проста. Кроме определений ваших собственных функций обратного вызова, использующих модули proc_lib
и sys
, вам потребуется запускать их вместе с конкретными функциями, обрабатывать системные сообщения и следить за тем, не завершился ли родительский процесс.
Продолжение статьи: Супервизоры.