Предположим, у нас есть функция, которая обновляет пароль пользователя.
После нажатия кнопки «Обновить пароль» событие UpdatePasswordEvent отправляется в раздел, где подписаны 3 другие службы:
- Сервис, который фактически обновляет пароль пользователя
- Сервис, который обновляет историю паролей пользователя
- Служба, которая отправляет электронное письмо, информирующее пользователя об изменении его пароля.
Исходя из того, что я понял о возможной согласованности, все эти службы (потребители) будут получать событие одновременно и обрабатывать их отдельно, что в хорошем сценарии приведет к согласованности данных.
Однако, что если службе не удается обработать событие? например, внезапное отключение, ошибка базы данных и т. д. Что такое хороший метод / практика для обработки этих сбоев транзакций?
Я думал о создании RollbackTopic, где, если какое-либо событие не будет обработано, будет создано RollbackEvent в теме, где «службы отката» выполнят свою работу и вернут данные обратно.
Ответы:
Нет, не обязательно Как я уже говорил, мы не можем отменить отправленное письмо, поэтому нам все еще нужна своего рода «последовательность». IPC через управляемое событиями управление данными не освобождается от orchestation 1 .
Например, электронное письмо не следует отправлять, если предыдущие транзакции не были успешно завершены, а служба электронной почты получила подтверждение. 3
Скажите привет ошибкам распределенных вычислений . Это то, что усложняет ситуацию, и, как обычно, нет серебряных пуль, чтобы иметь с ними дело.
Прежде чем отправиться в путешествие в поисках Потерянного Ковчега, мы должны сначала спросить организацию. Часто решение состоит в том, как организация сталкивается с этими проблемами в реальном мире .
Что каждый (отделы) делает, когда определенные данные отсутствуют или неполны?
Мы поймем, что разные отделы имеют разные решения, которые, в целом, составляют решение, которое будет реализовано.
Во всяком случае, вот некоторые практики, которые могли бы помочь нам со стратегией для подражания.
Возможная последовательность
Вместо того, чтобы постоянно гарантировать, что система находится в согласованном состоянии, мы можем согласиться с тем, что система получит ее в какой-то момент в будущем. Этот подход особенно полезен для долгоживущих деловых операций.
Способ достижения системой согласованности варьируется от системы к системе. Это может включать в себя от автоматизированных процессов до какого-то вмешательства человека. Например, типичная попытка повторить позже или контакт со службой поддержки .
Прервать все операции
Верните систему в согласованное состояние с помощью компенсирующих транзакций . Тем не менее, мы должны принять во внимание, что эти транзакции также могут потерпеть неудачу, что может привести нас к точке, в которой несоответствие еще сложнее решить. И, опять же, мы не можем отменить отправленное письмо.
Для небольшого количества транзакций такой подход возможен, потому что число компенсирующих транзакций также мало. Если бы в IPC участвовало несколько бизнес-транзакций, обработка одной компенсирующей транзакции для каждой из них была бы сложной задачей.
Если мы пойдем на компенсационные транзакции , мы найдем схему проектирования автоматического выключателя очень полезной - и обязательной, я бы осмелился сказать -
Распределенные транзакции
Идея состоит в том, чтобы охватить несколько транзакций в рамках одной транзакции посредством общего процесса управления, известного как Transaction Manager . Распространенным алгоритмом обработки распределенных транзакций является двухфазная фиксация .
Основная проблема распределенных транзакций заключается в том, что они полагаются на блокировку ресурсов в течение срока их службы, и, как мы знаем, что-то может пойти не так и для менеджера транзакций .
Если менеджеры транзакций будут скомпрометированы, мы можем получить несколько блокировок в разных контекстах, что приведет к неожиданному поведению из-за постановки сообщений в очередь. 2
Разложение операций. Зачем?
В соответствии с приведенными выше аргументами Сэм - в своей книге « Построение микросервисов» - заявляет, что, если мы действительно, действительно не можем позволить себе возможную последовательность, нам следует избегать разделения операции сейчас.
Если мы не можем позволить себе разделение определенных операций на две или более транзакций, то можно сказать, что, вероятно, эти транзакции относятся к одному и тому же ограниченному контексту или, по крайней мере, к сквозному контексту, который еще предстоит смоделировать.
Например, в нашем случае мы понимаем, что транзакции № 1 и № 2 тесно связаны друг с другом и, вероятно, обе они могут принадлежать к одному и тому же ограниченному контексту: учетные записи , пользователи , регистр , что угодно ...
Рассмотрите возможность размещения обеих операций в пределах одной транзакции. Это сделало бы всю операцию проще в обращении. Также весит уровень критичности каждой транзакции. Вероятно, в случае сбоя транзакции № 2 она не должна ставить под угрозу всю операцию. В случае сомнений обращайтесь в организацию .
1: Не тот тип оркестровки, как вы думаете. Я не говорю об оркестре ESB. Я говорю о том, чтобы заставить службы реагировать на соответствующее событие.
2: Вы могли бы найти интересные мнения Сэма Ньюмана относительно распределенных транзакций.
3: Проверьте ответ Дэвида Паркера относительно этой темы.
источник
В вашем случае вы не можете просто обработать все три вещи одновременно. То, что вам нужно, это процесс. Вот чрезвычайно упрощенный пример:
Важно знать, что операции по изменению состояния ДОЛЖНЫ всегда выполняться на согласованном объекте. Если вы не можете гарантировать строгую согласованность , это должно быть сделано в основной записи.
Ваша система должна гарантировать, что до того, как произойдет какое-либо событие в вашей системе, изменения ДОЛЖНЫ быть сохранены в первую очередь с безопасностью транзакций. Это делается для того, чтобы поднятое событие действительно было подтверждением того, что действительно произошло.
Есть несколько хитрых частей процесса, как есть, и я собираюсь игнорировать очевидные, такие как: Что, если ваш сервер базы данных умрет, сохраняя пользователя с измененным паролем? Вы просто снова запускаете UpdatePassword. Тем не менее, вы должны позаботиться о некоторых деталях, а именно:
В системе, оркестратор процессов (PO) - это не что иное, как другая сущность, которая содержит внутреннее состояние - в буквальном смысле этого слова - и позволяет осуществлять переходы между состояниями, эффективно выступая в качестве своего рода конечного автомата. Благодаря внутреннему состоянию вы можете удалить обработку дублирования сообщений.
Когда ПО находится в
New
состоянии и обрабатываетUserPasswordHasBeenUpdated
, оно меняет свое состояние наUserPasswordHasBeenUpdated
(или какое имя состояния подходит для вас). Если PO все еще находится в a,UserPasswordHasBeenUpdated
и другоеUserPasswordHasBeenUpdated
прибыло, PO полностью игнорировал бы сообщение, зная, что это дублирование. Аналогичный механизм будет реализован и для других государств.Обработка фактической отправки электронной почты немного сложнее. Здесь у вас есть два варианта:
Отправьте его максимум один раз
С этой опцией, когда достигнуто
UserPasswordHistoryHasBeenSaved
состояние заказа на поставку, команда для отправки электронной почты отправляется как реакция на изменение состояния. Ваша система будет гарантировать сохранениеUserPasswordHistoryHasBeenSaved
состояния перед отправкой электронной почты, т. Е. Дублированное сообщение не будет вызывать повторную отправку электронной почты. При таком подходе вы гарантируете, что правильное состояние сохранено для PO, но не можете гарантировать любую следующую операцию.Отправить его хотя бы один раз
Это то, что я хотел бы пойти.
Вместо того, чтобы сохранять
UserPasswordHistoryHasBeenSaved
и отправлять электронную почту в качестве реакции на нее, вы сначала пытаетесь отправить электронную почту. Если операция отправки терпит неудачу, состояние PO никогда не изменяется,UserPasswordHistoryHasBeenSaved
и другое сообщение того же типа все еще обрабатывается. Если отправка сообщения электронной почты действительно удалась, но ваша система потерпела бы неудачу во время сохранения PO с новымUserPasswordHistoryHasBeenSaved
состоянием, другое сообщениеUserPasswordHistoryHasBeenSaved
снова вызовет команду для отправки сообщения электронной почты, и пользователь получит ее несколько раз. ,В вашем случае вы хотите убедиться, что пользователь действительно получает электронную почту. Поэтому я бы выбрал второй вариант, а не первый.
источник
Системы очередей не так хрупки, как вы думаете.
Если бы мы записывали все три процесса в реляционную базу данных, мы могли бы использовать транзакцию для обработки сбоя промежуточных процессов.
Без окончательной фиксации частичная работа будет отброшена.
В системе с базой очередей у вас будут аналогичные опции при чтении сообщения из очереди для обработки промежуточных сбоев процесса.
Amazon SQS, например, просто скрывает сообщения, которые читаются. если не будет отправлена последняя команда удаления, сообщение появится снова или будет помещено в очередь недоставленных сообщений.
Вы можете осуществлять подобные «транзакции» различными способами, по существу, удерживая копию сообщения, пока не получите подтверждение успешной обработки. Если подтверждение не получено вовремя. Вы можете отправить сообщение еще раз или оставить его для ручного обращения.
Потенциально вы могли бы создать «сервис отката», который отслеживал эти ошибочные сообщения, знал о связанных сообщениях и прошлом состоянии и выполнял откат.
Тем не мение! Обычно лучше просто отправить сообщения с ошибками. Ведь это, как правило, крайние случаи. Либо произошел катастрофический сбой сервера, либо произошла ошибка при обработке определенного типа сообщения.
После оповещения об ошибке сервис может быть восстановлен и сообщения успешно обработаны. Приведение системы в целом обратно в согласованное состояние.
источник
Здесь вы столкнулись с проблемой двух генералов . По сути: как вы можете быть уверены, что сообщение получено и ответ на него получен? Во многих случаях идеального решения не существует. Фактически, в распределенной системе часто невозможно получить единовременную доставку сообщений.
Первое очевидное замечание заключается в том, что служба, которая изменяет пароль, должна отправлять событие смены пароля. Таким образом, история паролей и сервисы отправки почты запускаются только тогда, когда пароль действительно изменяется, независимо от того, почему он изменился.
Чтобы действительно решить вашу проблему, я бы не рассматривал распределенные транзакции, а вместо этого смотрел бы в сторону хотя бы раз доставки сообщений и идемпотентной обработки.
Хотя бы один раз
Чтобы убедиться, что событие смены пароля действительно видно всем потребителям, вам необходимо использовать надежный канал связи, где сообщения могут использоваться в стиле «хотя бы один раз». Потребители подтверждают сообщение как использованное, только когда полностью его обработали. Например, если во время записи записи журнала происходит сбой службы истории паролей, после перезапуска она перечитывает то же событие изменения пароля и повторяет попытку, признавая это событие доступным только для чтения после того, как оно было записано в историю. Вам следует выбрать решение для очереди сообщений, основанное на его способности пересылать сообщения до тех пор, пока они не будут подтверждены.
идемпотентность
После достижения хотя бы однократной доставки возникает проблема дублирования действий, возникающих, когда сообщение было частично обработано до того, как получатель был прерван, а затем обработан позже. Это должно быть решено путем разработки каждого сервиса, чтобы он был идемпотентным. Либо записи, которые он выполняет, могут происходить многократно без неблагоприятных последствий, либо он хранит собственное хранилище того, какие действия он совершил, и избегает выполнения действия более одного раза. В случае отправки почты, вы обнаружите, что, вероятно, не стоит пытаться заставить ее вести себя идемпотентно и просто быть в порядке с время от времени отправкой почты дважды.
В любом случае, будьте осторожны, насколько микро вы делаете свои услуги. Действительно ли ваша служба истории паролей должна быть независимой от службы смены паролей?
источник
Я не согласен со многими ответами.
Есть и другие обещания согласованности, которые вы можете добавить.
Эти дополнительные согласования должны быть реализованы в зависимости от действий приложения.
Я понятия не имею, что вы подразумеваете под «обновляет историю», но, пожалуйста, никогда не меняйте историю. Если вы просто расширяете DAG, это должно вызвать изменение текущего состояния. Они не независимы. Если они есть, то вы не можете полагаться на историю, отражающую то, что произошло. (и последнее, но не менее важное: не храните пароли, посмотрите, как не хранить пароли )
источник
consider asking the organization first.
. Вы, вероятно, правы. Однако я обнаружил, что важно обуславливать те события, которые мы не можем отменить. Например, уведомления для конечного пользователя. Уведомление о реальном состоянии данных пользователя может привести к плохому впечатлению.