Интеграция RabbitMQ с транзакциями базы данных
Представьте себе ситуацию:
var txn = new DatabaseTransaction();
var entry = txn.Database.Load<Entry>(id);
entry.Token = "123";
txn.Database.Update(entry);
PublishRabbitMqMessage(new EntryUpdatedMessage { ID = entry.ID });
// A bit more of processing
txn.Commit();
Теперь потребитель EntryUpdatedMessage
потенциально может получить это сообщение до транзакции txn
зафиксировано и, следовательно, не сможет увидеть обновление.
Теперь я знаю, что RabbitMQ поддерживает транзакции сам по себе, но мы не можем их использовать, потому что мы создаем новую IModel
для каждой публикации и наличия модели для каждого потока в нашем сценарии действительно сложно (веб-приложение ASP.NET).
Я подумал о том, чтобы иметь список сообщений, которые должны быть опубликованы при фиксации транзакции с БД, но это действительно вонючее решение.
Как правильно справиться с этим?
1 ответ
RabbitMQ рекомендует вам использовать подтверждения издателя, а не транзакции. Транзакции не работают хорошо.
В любом случае транзакции обычно не очень хорошо работают с сервис-ориентированной архитектурой. Лучше принять "в конечном итоге непротиворечивый" подход, при котором сбой может быть повторен позднее, а дублированные идемпотентные сообщения игнорируются.
В вашем примере я бы сделал обновление базы данных и зафиксировал его перед публикацией сообщения. Когда издатель подтвердит возврат, я обновлю поле в записи базы данных, чтобы указать, что сообщение было отправлено. Затем вы можете запустить процесс очистки, проверить наличие неотправленных сообщений и отправить их по пути. Если сообщение все-таки прошло, но по какой-то причине подтверждение или последующая запись в базу данных не удалось, вы получите дубликат сообщения. Но это не имеет значения, потому что вы создали свои сообщения как идемпотенты.