Ответ 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 обычно используется для описания баз данных, но я считаю, что подобные вопросы нужно задавать, когда вам требуется определенный уровень отказоустойчивости при обработке данных.