Сообщения действительно ли Erlang/OTP надежны? Сообщения могут быть дублированы?

Долгая версия:

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

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

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

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

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

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

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

36
задан joshng 8 January 2014 в 00:14
поделиться

2 ответа

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


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

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

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

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

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

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


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

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

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

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

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


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

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

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

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

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


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

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

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

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

  1. Согласованность
  2. Допуск на разделение
  3. Доступность

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

110
ответ дан 27 November 2019 в 05:15
поделиться

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

  1. Когда процесс умирает, он будет перезапущен.
  2. По большей части падение процесса не приведет к краху всего приложения
  3. Когда сообщение отправлено, оно будет получено, если существует получатель.

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

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

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

4
ответ дан 27 November 2019 в 05:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: