Что такое OTP и поведения
Зачем Erlang нужен фреймворк поверх процессов и сообщений.
OTP (Open Telecom Platform) — набор библиотек, принципов и шаблонов проектирования для построения промышленных отказоустойчивых систем на Erlang.
Мы умеем вручную создавать процессы, слать сообщения, ловить падения. Но писать это руками каждый раз — утомительно и чревато ошибками. OTP закрепляет проверенные временем шаблоны в готовых строительных блоках. На практике «писать на Erlang» почти всегда означает «писать на OTP». Название расшифровывается как Open Telecom Platform и выдаёт происхождение: фреймворк родился в недрах телекоммуникационной компании Ericsson, где системы должны были работать годами без остановки и переживать сбои оборудования. Слово «Telecom» в имени сегодня скорее историческое — OTP давно применяют далеко за пределами телекома, от веб-серверов до систем обмена сообщениями вроде того, что внутри WhatsApp. Главное в OTP — не конкретные функции, а способ мышления: вы не изобретаете заново механику надёжного процесса, а заполняете готовый, выверенный каркас своей бизнес-логикой.
Проблема ручных серверов
Серверный цикл из прошлого раздела работает, но в нём не хватает многого: обработки системных сообщений, корректного завершения, отладочных средств, обновления кода на лету. Если каждый разработчик будет дописывать это сам, получится разнобой и баги. OTP решает: давайте напишем эту инфраструктуру один раз и качественно. Подумайте, сколько неочевидных вопросов возникает у «всего лишь» серверного процесса. Как он должен реагировать на запрос остановки от системы? Что делать, если кто-то хочет заглянуть в его состояние для отладки? Как корректно завершиться, освободив ресурсы, а не просто исчезнуть? Как обновить его код, пока он работает, не теряя накопленное состояние? Каждый из этих вопросов имеет правильный, но неочевидный ответ, выстраданный годами эксплуатации. Самостоятельно написанный цикл почти наверняка проигнорирует половину из них — и проблемы всплывут в самый неподходящий момент, в продакшене под нагрузкой. OTP уже содержит верные ответы, и пользуясь поведением, вы получаете их даром.
Что такое поведение (behaviour)
Поведение — это разделение кода на две части: общую (универсальную, написанную авторами OTP) и прикладную (вашу, специфичную для задачи). Общая часть знает, как принимать сообщения, обрабатывать сбои, завершаться. Прикладная часть отвечает только на вопрос «а что делать с конкретным запросом».
+------------------------+
| Общая часть (OTP) | <- цикл, приём, системные сообщения
| написана один раз |
+------------------------+
| Ваши callback-функции | <- бизнес-логика
| handle_call и т.п. |
+------------------------+
Вы реализуете набор функций обратного вызова (callbacks) с фиксированными именами, а OTP вызывает их в нужный момент. Это похоже на заполнение шаблона: каркас готов, вы вписываете содержание. Здесь работает так называемый «принцип Голливуда»: «не звоните нам, мы позвоним вам». Вы не управляете циклом сами и не решаете, когда вызвать обработку очередного сообщения, — наоборот, общая часть владеет управлением и сама обращается к вашим функциям в подходящий момент. Это инверсия контроля, и она ключевая: именно потому, что OTP держит штурвал, она может гарантировать корректное поведение во всех системных ситуациях, не полагаясь на то, что вы не забыли что-то предусмотреть. Ваша свобода ограничена рамками контракта callback'ов, и эта ограниченность — не недостаток, а источник надёжности.
Основные поведения OTP
| Поведение | Для чего |
gen_server | сервер с состоянием — главный рабочий блок |
supervisor | надзор за дочерними процессами и перезапуск |
gen_statem | конечный автомат (машина состояний) |
application | упаковка модулей в запускаемое приложение |
Объявление поведения
Модуль заявляет, какое поведение реализует, директивой -behaviour. Компилятор тогда проверит, что все обязательные callbacks на месте.
-module(my_server).
-behaviour(gen_server).
%% Обязательные callbacks
-export([init/1, handle_call/3, handle_cast/2]).
Директива пишется через behaviour или behavior — обе формы написания допустимы, что отражает британское и американское правописание; выбирайте любую, но придерживайтесь её единообразно в проекте. Если вы объявили поведение, но забыли реализовать обязательный callback, компилятор выдаст предупреждение, указав, какой именно функции не хватает. Это ранняя страховка: ошибку видно ещё на этапе сборки, а не в рантайме под нагрузкой.
Почему это надёжно
Общая часть OTP вылизана годами эксплуатации в телекоме и боевых системах. В ней учтены тонкости конкурентности, завершения, обновления кода, отладки. Используя поведение, вы автоматически получаете эту зрелость, не повторяя чужих ошибок. Ваш код становится короче и сосредоточен на сути задачи. Есть и менее очевидная, но огромная выгода — единообразие. Когда вся команда пишет процессы на одних и тех же поведениях, любой gen_server в кодовой базе устроен одинаково: те же callback'и, те же возвращаемые кортежи, та же логика запуска. Новый человек, открыв незнакомый модуль, мгновенно понимает его скелет и сразу смотрит только на прикладную часть. Это резко снижает стоимость сопровождения больших систем. Самописные серверы, наоборот, каждый раз разные, и их приходится изучать с нуля. Стандартизация на уровне архитектуры — недооценённое, но мощное свойство OTP.
Как работает под капотом
Поведение — это, по сути, generic-модуль (например, gen_server), который запускает стандартный приёмный цикл. Получив сообщение, он по типу вызывает соответствующий ваш callback (handle_call, handle_cast, handle_info), берёт у него новое состояние и продолжает цикл. Все системные тонкости — поддержка sys-сообщений, трассировка, корректное завершение — реализованы в общей части и работают «бесплатно».
Частые ошибки
- Писать собственные серверные циклы в проде. Почти всегда лучше взять
gen_server. - Не реализовать обязательный callback. Компилятор предупредит — не игнорируйте.
- Смешивать общую и прикладную логику. Ваша задача — только callbacks, остальное делает OTP.
Итоги
- OTP — набор библиотек и шаблонов для промышленных систем на Erlang.
- Поведение разделяет код на общую часть OTP и ваши callbacks.
- Главные поведения:
gen_server,supervisor,gen_statem,application. - Используя OTP, вы получаете годами проверенную инфраструктуру надёжности.