Подтвердить что ты не робот

Как вы проектируете архитектуру распределенной отказоустойчивой многоядерной системы на основе Erlang/OTP?

Я хотел бы создать систему на основе Erlang/OTP, которая решает проблему "смущающего parrallel".

Я уже прочитал/просмотрел:

  • Узнайте, что у вас есть Erlang;
  • Программирование Erlang (Armstrong);
  • Программирование Erlang (Cesarini);
  • Erlang/OTP в действии.

У меня есть суть процессов, сообщений, супервизоров, gen_servers, ведения журнала и т.д.

Я понимаю, что некоторые варианты архитектуры зависят от приложения, которое вызывает беспокойство, но все же я хотел бы знать некоторые общие принципы проектирования системы ERlang/OTP.

Должен ли я начать с нескольких gen_servers с помощью супервизора и постепенно наращивать его?

Сколько у меня руководителей? Как я могу решить, какие части системы должны быть основаны на процессах? Как следует избегать узких мест?

Должен ли я добавить запись позже?

Каков общий подход к распределенной архитектуры отказоустойчивых многопроцессорных систем Erlang/OTP?

4b9b3361

Ответ 1

Должен ли я начать с нескольких gen_servers с помощью супервизора и постепенно наращивать его?

В архитектуре Erlang отсутствует один ключевой компонент: приложения! (То есть, концепция приложений OTP, а не программных приложений).

Подумайте о приложениях как компонентах. Компонент в вашей системе решает определенную проблему, отвечает за согласованный набор ресурсов или абстрагирует что-то важное или сложное из системы.

Первый шаг при проектировании системы Erlang - решить, какие приложения необходимы. Некоторые из них можно вытащить из Интернета, как они есть, и мы можем назвать их библиотеками. Другим вам нужно написать себя (иначе вам не нужна эта конкретная система). Эти приложения мы обычно называем бизнес-логикой (часто вам нужно также писать некоторые библиотеки, но полезно держать различие между библиотеками и основными бизнес-приложениями, которые связывают все вместе).

Сколько у меня руководителей?

У вас должен быть один диспетчер для каждого вида процесса, который вы хотите контролировать.

Буква идентичных временных работников? Один руководитель, чтобы управлять ими всеми.

Разный процесс с различными обязанностями и стратегиями перезапуска? Супервизор для каждого процесса различного типа в правильной иерархии (в зависимости от того, когда вещи должны перезапускаться и какой другой процесс должен с ними спуститься?).

Иногда бывает хорошо поставить кучу разных типов процессов под одним и тем же супервизором. Обычно это происходит, когда у вас есть несколько однотонных процессов (например, один диспетчер HTTP-сервера, один владелец таблицы ETS, один сборщик статистики), который всегда будет работать. В этом случае, может быть, слишком много крутизны, чтобы иметь каждого наблюдателя для каждого, поэтому принято добавлять под одним супервизором. Просто имейте в виду последствия использования конкретной стратегии перезапуска при этом, поэтому вы не снимаете свой процесс статистики, например, в случае сбоя вашего веб-сервера (one_for_one- наиболее распространенная стратегия для использования в таких случаях). Будьте осторожны, чтобы не иметь зависимости между процессами в супервизоре one_for_one. Если процесс зависит от другого разбившегося процесса, он может также сбой, слишком часто запуская интенсивность перезапуска супервизора и слишком рано крушить супервизора. Этого можно избежать, если у вас есть два разных супервизора, которые полностью контролируют перезагрузки с помощью настроенной интенсивности и периода (более подробное объяснение).

Как мне решить, какие части системы должны быть основаны на процессах?

Каждая параллельная деятельность в вашей системе должна быть в ее собственном процессе. Неправильная абстракция concurrency является самой распространенной ошибкой дизайнеров системы Erlang в начале.

Некоторые люди не привыкли иметь дело с concurrency; их системы, как правило, слишком мало. Один процесс, или несколько гигантских, который запускает все последовательно. Эти системы, как правило, полны запаха кода, а код очень жесткий и трудно рефакторирован. Это также делает их медленнее, потому что они не могут использовать все ядра, доступные для Erlang.

Другие люди сразу понимают концепции concurrency, но не могут применять их оптимально; их системы, как правило, злоупотребляют концепцией процесса, заставляя многие процессы оставаться без дела в ожидании других, которые выполняют работу. Эти системы, как правило, излишне сложны и трудно отлаживаются.

В сущности, в обоих вариантах вы получаете ту же проблему, вы не используете все доступные concurrency и не получаете максимальную производительность из системы.

Если вы придерживаетесь принципа принципа единой ответственности и соблюдаете правило, чтобы иметь процесс для каждого действительного одновременного действия в вашей системе, вы должно быть хорошо.

Имеются веские причины для простоя процессов. Иногда они сохраняют важное состояние, иногда вы хотите временно сохранить некоторые данные и позже отказаться от процесса, иногда они ждут внешних событий. Большая ошибка заключается в передаче важных сообщений через длинную цепочку в основном неактивных процессов, так как это замедлит работу вашей системы с большим количеством копирования и использования большего объема памяти.

Как избежать узких мест?

Трудно сказать, сильно зависит от вашей системы и того, что она делает. В общем, хотя, если у вас есть хорошее разделение ответственности между приложениями, вы должны иметь возможность масштабировать приложение, которое является узким местом отдельно от остальной системы.

Золотое правило здесь - измерять, измерять, измерять! Не думайте, что вам есть что улучшить, пока вы не измерили.

Erlang замечателен тем, что позволяет скрывать concurrency за интерфейсами (известный как неявный concurrency). Например, вы используете API функционального модуля, обычный module:function(Arguments) интерфейс, который может, в свою очередь, порождать тысячи процессов без того, чтобы вызывающий пользователь знал об этом. Если вы получили свои абстракции и права API, вы всегда можете распараллелить или оптимизировать библиотеку после того, как вы ее начали использовать.

Говоря это, вот некоторые общие направляющие строки:

  • Попробуйте напрямую отправлять сообщения получателю, избегайте направления или маршрутизации сообщений через посреднические процессы. В противном случае система просто тратит время на перемещение сообщений (данных) без реальной работы.
  • Не злоупотребляйте шаблонами проектирования OTP, например gen_servers. Во многих случаях вам нужно только запустить процесс, запустить часть кода, а затем выйти. Для этого gen_server переполнен.

И один бонусный совет: не используйте повторно процессы. Нерест процесс в Эрланге настолько дешев и быстр, что не имеет смысла повторно использовать процесс, как только закончится его жизнь. В некоторых случаях может возникнуть смысл повторно использовать состояние (например, сложный анализ файла), но лучше канонически хранить в другом месте (в таблице ETS, базе данных и т.д.).

Должен ли я добавить запись позже?

Там уже есть основные функции ведения журнала в Erlang/OTP, журнал ошибок. Вместе с SASL (библиотеки поддержки системной архитектуры) вы можете запускаться и запускаться без регистрации.

Когда наступит время (и если вы отбросили API регистрации с самого начала), вы можете обменять его на то, что лучше соответствует вашим потребностям. Сегодня фактическая библиотека регистрации третьей стороны Basho Lager.

Каков общий подход к распределенной архитектуре отказоустойчивых многопроцессорных систем Erlang/OTP?

Подводя итог сказанному выше:

  • Разделите свою систему на приложения
  • Поместите ваши процессы в правильную иерархию наблюдения, в зависимости от их потребностей и зависимостей.
  • У вас есть процесс для каждой действительной одновременной работы в вашей системе.
  • Поддерживать функциональный API для других компонентов системы. Это позволяет:
    • Рефакторинг кода без изменения кода, использующего его
    • После этого оптимизируйте код
    • Распределите свою систему при необходимости (просто позвоните в другой node за API! Абонент не заметит!)
    • Протестируйте код более легко (меньше работы по настройке тестовых жгутов, проще понять, как его использовать).
  • Начните использовать библиотеки, доступные вам в OTP, пока вам не понадобится что-то другое (вы узнаете, когда придет время)

Общие ошибки:

  • Слишком много процессов
  • Слишком мало процессов
  • Слишком много маршрутизации (перенаправленные сообщения, связанные процессы)
  • Слишком мало приложений (на самом деле я никогда не видел обратного случая)
  • Недостаточно абстракции (затрудняет рефакторинг и рассуждение, а также сложно проверить!)