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

Надежны ли сообщения Erlang/OTP? Можно ли дублировать сообщения?

Длинная версия:

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

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

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

(Я признаю, что эти проблемы не уникальны для erlang, аналогичные проблемы возникнут в любой распределенной системе обработки. Но энтузиасты erlang, похоже, утверждают, что платформа делает все это "легким"..?)

Предполагая, что сообщения повторно передаются, я могу легко представить себе сценарий, когда последующие эффекты сложной цепочки сообщений могут стать очень запутанными после сбоя. Без какой-либо тяжелой распределенной транзакционной системы я не понимаю, насколько совместимость и правильность могут поддерживаться без устранения дублирования в каждом процессе. Должен ли мой код приложения всегда применять ограничения для предотвращения выполнения транзакций более одного раза?

Краткая версия:

Распределены процессы erlang, подлежащие дублированию сообщений? Если это так, является дубликат-защита (т.е. idempotency) ответственность приложения или erlang/OTP каким-то образом помогают нам в этом?

4b9b3361

Ответ 1

Я разберу это в пункты, которые, я надеюсь, будет иметь смысл. Возможно, я немного переработал то, что я написал в Руководство автостопом по Concurrency. Возможно, вам захочется прочитать это, чтобы получить подробную информацию о обосновании того, как передача сообщений выполняется в Erlang.


1. Передача сообщений

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

Случайным примером этого может быть представление долговременного процесса, который сует данные в течение 4 часов. Должен ли он действительно признать, что он получил сообщение, если он не может его обработать? Может быть, это должно быть, а может и нет. Это действительно зависит от вашего приложения. Таким образом, предположения не принимаются. Вы можете иметь половину ваших асинхронных сообщений и только одну, которая не является.

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

Чтобы сделать его коротким, , не прочитано ли сообщение, не может быть получено или прервано, когда кто-то вытаскивает вилку, пока она находится в пути, не имеет значения, если вы этого не хотите. Если вы хотите, чтобы это имело значение, вам нужно разработать логику процессов.

Бремя реализации высокоуровневого протокола сообщений между процессами Erlang предоставляется программисту.


2. Протоколы сообщений

Как вы сказали, эти сообщения хранятся в переходной памяти: если процесс умирает, все сообщения, которые он еще не прочитал, теряются. Если вы хотите больше, существуют различные стратегии. Некоторые из них:

  • Прочитайте сообщение как можно быстрее и напишите его на диск, если необходимо, отправьте подтверждение и обработайте его позже. Сравните это с программным обеспечением очереди, таким как RabbitMQ и ActiveMQ, с постоянными очередями.
  • Используйте группы процессов для дублирования сообщений по группе процессов на нескольких узлах. На этом этапе вы можете ввести транзакционную семантику. Этот используется для базы данных mnesia для транзакционных коммитов;
  • Не предполагайте, что что-то сработало до тех пор, пока вы не получите подтверждение о том, что все прошло нормально или сообщение об ошибке
  • Комбинация групп процессов и сообщений об отказах. Если первый процесс не справляется с задачей (поскольку параметр node падает), уведомление автоматически отправляется виртуальной машиной на процесс отказа, который обрабатывает его. Этот метод иногда используется с полными приложениями для обработки отказов оборудования.

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

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


3. Что такое отказоустойчивость

Выбор одной из вышеперечисленных стратегий зависит от того, что означает отказ от толерантности. В некоторых случаях люди говорят, что "никакие данные никогда не теряются, никакая задача никогда не терпит неудачу". Другие люди используют отказоустойчивость, чтобы сказать: "пользователь никогда не видит сбоя". В случае систем Erlang обычное значение заключается в поддержании работы системы: вполне возможно, что у одного пользователя есть телефонный звонок, а не на то, чтобы все его сбросили.

Здесь идея состоит в том, чтобы позволить материалу, который терпит неудачу, но оставит все остальное. Для достижения этой цели вам дается несколько вещей:

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

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


4. Несколько примечаний

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

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

Обычно это сводится к теореме CAP, которая в основном дает вам 3 варианта, из которых вам нужно выбрать два:

  • Согласованность
  • Допуск на разделение
  • Наличие

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

Ответ 2

Система OTR erlang отказоустойчива. Это не освобождает вас от необходимости создавать в нем одинаково отказоустойчивые приложения. Если вы используете erlang и OTP, то есть несколько вещей, на которые вы можете положиться.

  • Когда процесс завершится, этот процесс будет перезапущен.
  • По большей части процесс сбоя не приведет к сбою всего вашего приложения.
  • Когда сообщение отправлено, оно будет получено, если получатель существует.

Насколько я знаю, сообщения в erlang не подлежат дублированию. Если вы отправите сообщение, и процесс его получит, сообщение исчезнет из очереди. Однако, если вы отправляете сообщение, и процесс получает это сообщение, но сбой во время его обработки, это сообщение пропадает и не обрабатывается. Этот факт следует учитывать при разработке вашей системы. OTP помогает вам справиться с этим, используя процессы для изоляции критического кода инфраструктуры (например, супервизоры, gen_servers,...) из кода приложения, которое может быть подвержено сбоям.

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

Просто потому, что система отказоустойчива, не означает, что ваш алгоритм.

Ответ 3

Я думаю, что ответ никак не связан с Эрланг. Он основан на семантике взаимодействия клиент-сервер, где вы можете реализовать "как минимум один раз", "не более одного раза" или "ровно один раз" в вашем протоколе клиент-сервер. Все эти семантики вызова могут быть реализованы путем объединения уникальных тегов, повторов и регистрации клиентских запросов как на клиенте, так и на сервере перед его отправкой или выполнением, чтобы он мог быть захвачен сервером после сбоя. Помимо дубликатов вы можете потеряться, потерять или отложить сообщения.