Erlang: полное руководство по языку для параллельных систем с примерами кода

Подробное введение в язык программирования Erlang и платформу OTP. Объясняется уникальная философия «Let it crash», модель легковесных процессов и обмена сообщениями. Статья содержит многочисленные примеры кода, от базового синтаксиса до создания процессов, GenServer и Supervisor. Руководство демонстрирует, как Erlang позволяет создавать высокодоступные, отказоустойчивые и параллельные системы.
В мире, где многопоточность и отказоустойчивость становятся ключевыми требованиями, Erlang выделяется как уникальный инструмент, рожденный для решения сложнейших задач телекоммуникаций. Этот язык, созданный компанией Ericsson в 80-х, не просто синтаксис, а целая экосистема (OTP) для построения распределенных, отказоустойчивых, параллельных систем с высокой доступностью (99.9999999% — девять девяток!). Если ваше приложение должно работать вечно, переживая сбои железа и программные ошибки, Erlang — ваш выбор. Это руководство познакомит вас с его философией, основными концепциями и покажет их в действии.

Философия Erlang кратко выражена в принципе Джо Армстронга, одного из его создателей: «Пусть все падает» (Let it crash). Вместо того чтобы перегружать код проверками на каждую возможную ошибку, Erlang предлагает изолировать процессы и позволять им аварийно завершаться, а затем корректно перезапускать их под наблюдением специальных процессов-надзирателей (Supervisors). Это меняет парадигму разработки, делая системы не просто надежными, а самовосстанавливающимися.

Основа всего — легковесные процессы Erlang. Это не потоки операционной системы. Виртуальная машина BEAM (Bogdan/Björn’s Erlang Abstract Machine) может эффективно управлять миллионами таких процессов на одном ядре. Они изолированы, не разделяют память и общаются исключительно через асинхронную передачу сообщений (message passing). Это фундаментальное отличие от традиционных многопоточных моделей с общей памятью, устраняющее целый класс ошибок (race conditions, deadlocks).

Давайте начнем с кода. Синтаксис Erlang функциональный, с сильным влиянием Пролога. Определим модуль и простую функцию.

-module(math_utils).
-export([factorial/1]).

factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).

Здесь мы видим сопоставление с образцом (pattern matching): функция определяется несколькими клаузами. Вызов math_utils:factorial(5) вернет 120. Обратите внимание на объявление модуля и экспорта функции (указана ее арность — /1).

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

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

start() ->
 Pid = spawn(echo, loop, []), % Создаем новый процесс
 Pid ! {self(), hello}, % Отправляем сообщение (свои PID и атом hello)
 receive % Ожидаем ответ
 {Pid, Msg} -> io:format("Received: ~p~n", [Msg])
 after 2000 -> timeout
 end.

loop() ->
 receive % Процесс ждет сообщения
 {From, Msg} ->
 From ! {self(), Msg}, % Отправляем сообщение обратно отправителю
 loop(); % Рекурсивно продолжаем ждать
 stop -> ok % Сообщение для остановки
 end.

Вызов echo:start() создаст процесс, выполняющий функцию loop, отправит ему сообщение и получит ответ. Ключевые операторы: spawn (создание процесса), ! (отправка сообщения), receive (получение и сопоставление с образцом).

Мощь Erlang раскрывается в OTP (Open Telecom Platform) — наборе библиотек и паттернов. Его сердце — поведение (behaviours). Рассмотрим самое важное: GenServer (Generic Server). Это шаблон для создания серверных процессов с единым интерфейсом. Вот упрощенный пример кэш-сервера.

-module(cache).
-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, Value) -> gen_server:call(?MODULE, {put, Key, Value}).
get(Key) -> gen_server:call(?MODULE, {get, Key}).

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

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

handle_cast(_Msg, State) -> {noreply, State}.

GenServer инкапсулирует цикл приема сообщений, предоставляя колбэки (init, handle_call, handle_cast). Клиенты взаимодействуют с ним через четкие интерфейсные функции (put/2, get/1). Это стандартизация, которая является основой надежности.

Следующий элемент OTP — Supervisor. Он следит за дочерними процессами (например, нашими GenServer) и перезапускает их по заданной стратегии в случае падения.

-module(cache_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).

start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
 SupFlags = #{strategy => one_for_one, intensity => 3, period => 10},
 ChildSpec = #{
 id => cache,
 start => {cache, start_link, []},
 restart => permanent,
 shutdown => 2000
 },
 {ok, {SupFlags, [ChildSpec]}}.

Этот супервизор будет перезапускать процесс cache, если тот упадет, но не более 3 раз за 10 секунд (intensity/period). После этого супервизор сам остановится, и решение поднимется на уровень выше. Так строится иерархия «дерево отказоустойчивости».

Работа с ошибками и мониторинг также встроены в язык. Функция erlang:error(reason) генерирует исключение, которое можно перехватить с помощью try...catch. Но чаще всего процессы просто позволяют себе упасть, а супервизор их перезапускает.

Erlang также блестяще справляется с горячим обновлением кода (hot code swapping) без остановки системы, что критично для телекоммуникационных свитчей. Виртуальная машина BEAM поддерживает загрузку двух версий модуля одновременно, постепенно переводя процессы на новую.

Изучение Erlang — это инвестиция в мышление. Вы начинаете проектировать системы как наборы взаимодействующих, изолированных сущностей, которые могут безопасно падать и восстанавливаться. Это открывает двери к созданию RabbitMQ, WhatsApp, Discord и других систем, обрабатывающих миллионы одновременных соединений. Начните с основ процессов и обмена сообщениями, затем погрузитесь в OTP-поведения, и вы обретете инструмент для построения по-настоящему надежного программного обеспечения.
331 4

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

avatar
q5ecvq 27.03.2026
BEAM (виртуальная машина Erlang) — это чудо. Жаль, что Elixir сейчас забирает весь хайп, а прародитель в тени.
avatar
rw266w7 27.03.2026
— это гениально для надежных систем.
avatar
ne1pu5kf8ye3 27.03.2026
Девять девяток доступности — это впечатляет. Хотелось бы больше практических примеров по супервизорам.
avatar
8d4eo6y9 27.03.2026
Статья хорошая, но не раскрыта тема Hot Code Swapping. Это же уникальная фича для обновлений без остановки!
avatar
8l9boz7ysjq 28.03.2026
Интересно, но сложновато для старта. Синтаксис Prolog-стиля отпугивает, хотя OTP — мощный фреймворк.
avatar
lvw42ksoi42f 29.03.2026
После Go и Akka возвращаюсь к истокам. Концепция акторов в Erlang реализована наиболее чисто и просто.
avatar
j0wvxd3 29.03.2026
Для современных микросервисов есть более модные языки. Erlang выглядит как нишевое legacy-решение.
avatar
a26rptw5izm 29.03.2026
Писал на Erlang в телекоме. Его подход к изоляции процессов и
avatar
g4gukmqk2fyo 30.03.2026
Изучал его для личного проекта. Легкость создания тысяч параллельных потоков — главное преимущество!
Вы просмотрели все комментарии