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

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

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

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

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

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

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

Укороченная версия:

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

3 ответа

Решение

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


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

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

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

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

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

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


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

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

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

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

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


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

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

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

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

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


4. Несколько заметок

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам