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

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

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

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

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

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

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

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

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


person joshng    schedule 03.07.2010    source источник
comment
Насколько я понимаю, когда процесс умирает и возрождается, все, что он делал, но не было возвращено, должно быть переделано.   -  person James Black    schedule 03.07.2010
comment
Очевидно, что незавершенную работу нужно переделать... Я думаю, вы предполагаете, что перезапуск незавершенных задач лежит на приложении (предположительно, путем отслеживания завершения и повторной отправки неудачных сообщений). Это то, что вы имели ввиду? Может ли кто-нибудь подтвердить это на своем опыте?   -  person joshng    schedule 04.07.2010


Ответы (3)


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


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

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

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

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

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

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


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

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

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

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

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


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

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

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

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

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


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

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

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

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

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

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

person I GIVE TERRIBLE ADVICE    schedule 05.07.2010
comment
Нам нужен способ пожертвовать некоторые из наших баллов, чтобы повысить ответы более чем на +1. Серьезно хорошая работа здесь. - person JUST MY correct OPINION; 05.07.2010

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

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

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

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

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

person Jeremy Wall    schedule 04.07.2010

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

person Alexei Polkhanov    schedule 15.01.2011