Мир не стоит на месте. Прогресс создает новые технологические вызовы. В соответствии с изменившимися требованиями, должна эволюционировать и архитектура информационных систем. Сегодня мы будем говорить о событийно-ориентированной архитектуре, конкурентности, параллельности, асинхронности и о том, как в Erlang можно со всем этим жить мирно.
Введение
В зависимости от размеров проектируемой системы и требований к ней, мы, разработчики, выбираем способ обмена информацией в системе. В большинстве случаев, для организации взаимодействия сервисов рабочим вариантом может быть схема с брокером, например на основе RabbitMQ или kafka. Но иногда поток событий, SLA и уровень контроля над системой таковы, что готовый messaging нам не подходит. Конечно, можно немного усложнить систему, взяв ответственность за транспортный уровень и формирование кластера, например используя ZeroMQ или nanomsg. Но если системе хватает пропускной способности и возможностей стандартного Erlang кластера, то вопрос внесения дополнительной сущности требует подробного изучения и экономического обоснования.
Тема реактивных распределенных приложений довольно обширна. Чтобы уложиться в формат статьи, предметом сегодняшнего обсуждения будут только гомогенные среды, построенные на основе Erlang/Elixir. Экосистема Erlang/OTP позволяет реализовать реактивную архитектуру с наименьшими трудозатратами. Но в любом случае нам понадобится слой обмена сообщениями.
Теоретический базис
Проектирование начинается с определения целей и ограничений. Основная цель не находится в области разработки ради разработки. Нам необходимо получить безопасный и масштабируемый инструмент, на основе которого можно создавать, а самое главное – развивать современные приложения разного уровня: начиная от односерверных, обслуживающих небольшую аудиторию, которые в дальнейшем могут развиться в кластеры до 50-60 узлов, заканчивая федерациями кластеров. Таким образом основная цель – максимизация прибыли путем сокращения стоимости разработки и владения итоговой системой.
Выделим 4 главных требования к итоговой системе:
- Событийная ориентированность.
Система всегда готова пропускать через себя поток событий и производить необходимые действия; - Масштабируемость.
Отдельные блоки могут масштабироваться как вертикально, так и горизонтально. Вся система должна иметь возможность бесконечного горизонтального роста; - Отказоустойчивость.
Все уровни и все сервисы должны иметь возможность автоматического восстановления при сбоях; - Гарантированное время отклика.
Время ценно и пользователи не должны ждать слишком долго.
Помните старую сказку про “The little engine that could”, он же – “Паровозик, который смог”? Чтобы проектируемая система успешно вышла из стадии прототипа и была прогрессивной, ее фундамент должен удовлетворять минимальным требованиям СМОГ.
К messaging как к инфраструктурному инструменту и базису для всех сервисов добавляется еще один пункт: удобство использования для программистов.
Ориентированность на события
Чтобы приложение могло вырасти из одного сервера до кластера, его архитектура должна обеспечивать слабую связанность. Этому требованию отвечает асинхронная модель. В ней отправитель и получатель заботятся об информационной нагрузке сообщения и не беспокоятся за передачу и маршрутизацию внутри системы.
Масштабируемость
Масштабируемость и эффективность системы стоят рядом. Компоненты приложения должны уметь утилизировать все доступные ресурсы. Чем эффективнее мы можем утилизировать мощности и чем оптимальнее наши методы обработки, тем меньше мы тратим денег на оборудование.
В рамках одной машины Erlang создает высококонкурентную среду. Баланс между конкурентностью и параллельностью можно задать выбором количества потоков операционной системы доступных для Erlang VM и числом планировщиков утилизирующих эти потоки.
Erlang процессы не имеют общего состояния и работают в неблокирующем режиме. Это обеспечивает сравнительно низкую латентность и более высокую пропускную способность, чем у традиционных приложений построенных на блокирующей синхронизации. Планировщик Erlang заботится о справедливом распределении CPU и IO, а отсутствие блокировок позволяет приложению отвечать даже в режиме пиковых нагрузок или сбоев.
На уровне кластера проблема с утилизацией тоже существует. Важно, чтобы все машины в кластере были равномерно нагружены, а сеть не перегружена. Представим ситуацию: пользовательский трафик приземляется на входящие балансировщики (haproxy, nginx, etc), они максимально равномерно распределяют запросы на обработку между набором доступных бэкендов. В рамках инфраструктуры приложения сервис, реализующий требуемый интерфейс, – это только последняя миля, и ему нужно будет запросить ряд других сервисов, чтобы ответить на первоначальный запрос. Внутренние запросы тоже требуют маршрутизации и балансировки.
Чтобы эффективно управлять потоками данных, messaging должен предоставлять разработчикам интерфейс для управления маршрутизацией и распределением нагрузки. Благодаря этому, разработчики смогут, используя микросервисные паттерны (aggregator, proxy, chain, branch, etc), решать как стандартные задачи, так и редко возникающие.
С точки зрения бизнеса, масштабируемость – один из инструментов управления рисками. Главное удовлетворить запросы клиентов, оптимально используя оборудование:
- При увеличении мощности оборудования в результате прогресса. Оно не будет простаивать из-за несовершенства ПО. Erlang прекрасно масштабируется вертикально и всегда сможет утилизировать все ядра CPU и доступную память;
- В условиях облачных сред, мы можем управлять количеством оборудования в зависимости от текущей или прогнозируемой нагрузки и гарантировать SLA.
Отказоустойчивость
Рассмотрим две аксиомы: “Отказы недопустимы” и “Отказы будут всегда”. Для бизнеса отказ ПО – потеря денег, а что хуже – репутации. Балансируя между возможными потерями и стоимостью разработки отказоустойчивого ПО, часто можно найти компромисс.
В краткосрочной перспективе архитектура, в которой заложена отказоустойчивость, экономит деньги на покупке готовых решений кластеризации. Они стоят дорого, и в них тоже есть ошибки.
В долгосрочной, отказоустойчивая архитектура многократно окупает затраты на ее применение на всех этапах разработки.
Messaging внутри кодовой базы еще на этапе разработки позволяет детально проработать взаимодействие компонентов внутри системы. Этим упрощается задача реагирования и управления сбоями, так как все ответственные компоненты обрабатывают сбои, а итоговая система знает, как автоматически вернуться в штатное состояние после сбоя by design.
Отзывчивость
Вне зависимости от сбоев, приложение должно отвечать на запросы и удовлетворять SLA. Реальность такова, что люди не хотят ждать, соответственно бизнес должен подстроиться. От все большего числа приложений ожидают высокой отзывчивости.
Отзывчивые приложения работают в режиме, приближенном к реальному времени. Erlang VM функционирует в режиме мягкого реального времени. Для некоторых областей, таких как биржевая торговля, медицина, управление промышленным оборудованием, важен режим жесткого реального времени.
Отзывчивые системы улучшают UX и полезны бизнесу.
Предварительный итог
Планируя данную статью, я хотел поделиться опытом создания брокера обмена сообщениями и построением на его основе сложных систем. Но теоретическая и мотивационная часть получилась довольно обширной.
Во второй части статьи я расскажу про нюансы реализации точек обмена, шаблоны обмена сообщениями и их применение.
В третьей части рассмотрим общие вопросы организации сервисов, маршрутизации и балансировки. Поговорим о практической стороне масштабируемости и отказоустойчивости систем.
Конец первой части.
Фото @lucabravo.