Полное руководство по Erlang: от основ конкурентности к практике с примерами кода

Подробное введение в язык программирования Erlang и платформу OTP. Объясняются уникальные концепции: легковесные процессы, модель акторов, обмен сообщениями, супервизоры и отказоустойчивость. Статья содержит многочисленные примеры кода, от базового синтаксиса до реализации GenServer, и показывает, как Erlang используется для создания надежных распределенных систем.
Erlang — это не просто язык программирования. Это целая экосистема, созданная для построения отказоустойчивых, распределенных, высоконагруженных систем с «горячим» обновлением кода на лету. Изначально разработанный компанией Ericsson для телекоммуникационных коммутаторов, где простои недопустимы, Erlang нашел свое место в современных FinTech, IoT, мессенджерах (как WhatsApp) и игровых серверах. Его философия «пусть он падает» (let it crash) и модель акторов кардинально отличаются от привычных императивных языков. Это руководство проведет вас от базовых концепций к практическим примерам.

Краеугольный камень Erlang — это легковесные процессы и модель акторов. В отличие от потоков ОС, процессы Erlang невероятно дешевы в создании (пару килобайт памяти), их могут быть миллионы, и они изолированы друг от друга. У каждого процесса есть свой собственный mailbox (почтовый ящик). Процессы не разделяют память и взаимодействуют исключительно через асинхронную передачу сообщений. Это фундаментально устраняет целый класс проблем: гонки данных, deadlock-и, необходимость в сложных примитивах синхронизации. Если процесс падает (из-за ошибки), он не тянет за собой всю систему — его просто перезапускает специальный механизм наблюдения (supervisor).

Синтаксис Erlang может показаться необычным для тех, кто пришел с языков вроде Python или Java. Он функциональный, с динамической типизацией, и каждая инструкция заканчивается точкой (.). Переменные начинаются с заглавной буквы и могут быть присвоены только один раз (single assignment) в своей области видимости — это способствует написанию предсказуемого кода. Давайте начнем с классического «Hello World» и простой функции.

-module(hello).
-export([start/0]).

start() ->
 io:format("Hello, Erlang World!~n").

Мы объявляем модуль `hello`, экспортируем функцию `start/0` (где 0 — арность, количество аргументов), и она выводит строку. Компилируем в оболочке Erlang (erl): `c(hello).` и запускаем: `hello:start().`

Теперь создадим два процесса, которые обменяются сообщениями. Это суть модели акторов.

-module(echo).
-export([start/0, loop/0]).

start() ->
 Pid = spawn(echo, loop, []), % Создаем новый процесс, выполняющий функцию loop
 Pid ! {self(), "Hello, Process!"}, % Отправляем сообщение. `!` — оператор отправки.
 receive % Ждем ответа в почтовый ящик текущего процесса
 {From, Msg} ->
 io:format("Received echo: ~p from ~p~n", [Msg, From])
 after 2000 ->
 io:format("No reply...~n")
 end.

loop() ->
 receive % Процесс-эхо ждет сообщения в своем почтовом ящике
 {From, Msg} ->
 From ! {self(), Msg}, % Отправляем сообщение обратно отправителю
 loop(); % Рекурсивно продолжаем ждать новые сообщения
 stop ->
 io:format("Echo process stopping.~n")
 end.

Здесь `spawn` создает процесс. `Pid ! Message` отправляет сообщение. `receive ... end` блокирует процесс, пока в его почтовый ящик не придет сообщение, соответствующее одному из паттернов. Это мощный механизм сопоставления с образцом (pattern matching).

Теперь ключевая концепция надежности — супервизоры (Supervisors). Они создают и следят за дочерними процессами. Если дочерний процесс падает, супервизор по заданной стратегии (например, перезапустить один раз в секунду) решает, что делать. Это основание для иерархии «дерева процессов», где сбой на низком уровне локализуется и обрабатывается.

-behaviour(supervisor).

init([]) ->
 ChildSpec = #{id => echo_worker,
 start => {echo, start, []},
 restart => permanent,
 shutdown => 2000},
 {ok, { {one_for_one, 5, 10}, [ChildSpec]} }.

Это упрощенный пример спецификации ребенка для супервизора. Стратегия `one_for_one` означает: если падает один дочерний процесс, перезапускать только его.

Работа с бинарными данями — еще одна сильная сторона Erlang, критичная для сетевых протоколов. Бинарные данные заключаются в `>`.

parse_packet() ->
 io:format("Header: ~p, Length: ~p, Data: ~p~n", [Header, Length, Data]).

Здесь мы деконструируем бинарник по шаблону: первый байт в `Header`, следующие два байта в `Length`, а все остальное — в `Data`.

Наконец, OTP (Open Telecom Platform) — это набор библиотек и паттернов, которые формализуют лучшие практики Erlang. Это каркас для построения приложений. Ключевые элементы OTP: GenServer (универсальный сервер-процесс с состоянием), Application (точка входа и управления жизненным циклом), уже упомянутый Supervisor. Использование OTP — это переход от написания кода к сборке системы из надежных, стандартных компонентов.

Вот элементарный GenServer, хранящий значение:

-module(keyval_storage).
-behaviour(gen_server).
-export([start_link/0, put/2, get/1]).
-export([init/1, handle_call/3, handle_cast/2]).

start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
put(Key, Val) -> gen_server:call(?MODULE, {put, Key, Val}).
get(Key) -> gen_server:call(?MODULE, {get, Key}).

init([]) -> {ok, #{}}. % Инициализируем состояние как пустую map.

handle_call({put, Key, Val}, _From, State) ->
 NewState = maps:put(Key, Val, State),
 {reply, ok, NewState};
handle_call({get, Key}, _From, State) ->
 Reply = maps:get(Key, State, undefined),
 {reply, Reply, State}.

Это лишь вершина айсберга. Erlang требует смены парадигмы мышления, но взамен дает беспрецедентную надежность для определенного класса задач. Начните с экспериментов в оболочке, постройте простой чат-сервер из процессов, а затем погрузитесь в мир OTP. Вы откроете для себя язык, для которого отказ — не чрезвычайная ситуация, а часть нормального workflow.
447 3

Комментарии (14)

avatar
17e23iua 27.03.2026
.
avatar
ubrilsy 27.03.2026
Жду продолжения! Особенно интересны примеры с hot code swapping.
avatar
xuwwntdpmq 27.03.2026
Статья хороша, но не хватает сравнения производительности с Akka на JVM.
avatar
uzjf2c 27.03.2026
— это меняет мышление.
avatar
wfb5idnf5 27.03.2026
Отличное руководство! Как раз искал материал по акторам в Erlang для своего микросервиса.
avatar
nenax7f 27.03.2026
Elixir перетянул внимание, но знать корни — Erlang — обязательно.
avatar
ikj4iwkq 28.03.2026
Не согласен, что синтаксис — главная преграда. Проблема в узости сообщества и обучающих материалов.
avatar
ii3c9v 28.03.2026
Работаю с Erlang 5 лет. Автор верно подметил про философию
avatar
g2ih8itn4o 29.03.2026
Для новичка сложновато. Лучше начать с установки и
avatar
z7ez16e 29.03.2026
Слишком поверхностно про OTP. Без него в продакшене — никуда.
Вы просмотрели все комментарии