Исходный код вики Диллема обработки сообщений
Редактировал(а) Alexandr Fokin 2024/06/12 16:21
Скрыть последних авторов
author | version | line-number | content |
---|---|---|---|
![]() |
5.8 | 1 | | |(% style="width:929px" %)Ситуация: |
![]() |
5.3 | 2 | Имеется очередь, из которой приложение читает данные и обрабатывает их. Рассмотрим пример, что приложение имеет следующий цикл обработки сообщения: |
![]() |
2.1 | 3 | 1) Взять сообщение из очереди |
4 | 2) Попытаться выполнить некоторый набор действий на основе данных из сообщения. | ||
![]() |
5.8 | 5 | Отметим, что обработка сообщения может завершиться как успешно, так и с ошибкой.|(% style="width:539px" %) |
6 | | |(% style="width:929px" %)1] | ||
![]() |
2.1 | 7 | 1) Получаем сообщение |
![]() |
5.3 | 8 | 2) Выполняем коммит сообщения (при следующем чтении на вход пойдет следующее сообщение) |
![]() |
5.8 | 9 | 3) Выполняем обработку|(% style="width:539px" %)Мы теряем сообщение, в случае если его обработка не завершилась успешно. |
10 | | |(% style="width:929px" %)2] | ||
![]() |
2.1 | 11 | 1) Получаем сообщение |
12 | 2) Выполняем обработку | ||
![]() |
5.8 | 13 | 3) Если п.2 выполнен успешно, то выполняем коммит сообщения (при следующем чтении на вход пойдет следующее сообщение)|(% style="width:539px" %)В случае, если после выполнения 2 пункта наше приложение упадет (не успев выполнить пункт 3), то при повторном запуске мы обработаем то-же самое сообщение второй раз. |
14 | | |(% style="width:929px" %)((( | ||
![]() |
2.1 | 15 | Вопросы |
16 | 1) Является ли повторная обработка одного и того же сообщения допустимой для нашей системы. | ||
![]() |
5.1 | 17 | 2) Является ли потеря данных из одного из сообщений критичной для нашей системы. |
![]() |
5.3 | 18 | 3) Возможно в нашей системе производитель сообщение, генерирует сообщени3) Возможно в нашей системе производитель сообщение, генерирует сообщен3) Возможно в нашей системе производитель сообщение, генерирует сообщение повторно через некоторой промежуток времени, если фиксирует, что предыдущее сообщение не было обработано. |
![]() |
5.8 | 19 | )))|(% style="width:539px" %) |
20 | | |(% style="width:929px" %)Более надежное, но более тяжелое решение: | ||
21 | Использование подхода номер 2 совместно с каким-либо более продвинутым механизмом транзакций. Т.е в случае падения приложения транзакция не будет завершена успешно. | ||
![]() |
4.1 | 22 | Но есть риск если у нас выполняются 2 действия: |
![]() |
3.1 | 23 | 1) коммит транзакции в базе, 2) коммит сообщения. (или в порядке 2, 1) |
24 | В случае падения приложения между указанными шагами, мы все равно можем получить | ||
![]() |
5.3 | 25 | либо потерю сообщения (закоммитили сообщение, но не завершили транзакцию), |
![]() |
4.1 | 26 | либо повторную обработку (завершили транзакцию, но не закоммитил сообщение). |
![]() |
5.8 | 27 | Хоть и вероятность такого события в целом крайне мала. (зависит от системы)|(% style="width:539px" %) |
28 | | |(% style="width:929px" %)((( | ||
![]() |
5.3 | 29 | Замечание: на текущий момент отношу проблему к [[Dual write problem>>doc:Архитектура и модели.Группа\. Распределенные системы.Распределенные системы\. Консистентность.Dual write problem.WebHome]]. |
![]() |
5.5 | 30 | По хорошему у каждого запроса или хотя бы сообщения должен быть уникальный ключ. Использую его, транзакцию, таблицу с уникальным индексом можно добиться гарантии, что сообщение будет обработано только единожды. Транзакция БД позволяет атомарно зафиксировать и изменения и факт обработки сообщения (по его ключу). |
![]() |
2.1 | 31 | |
![]() |
5.4 | 32 | Отдельный вопрос: хотим ли мы использовать очередь для приостановки обработки в случае ошибок. Зачастую мы не хотим останавливать обработку из-за одной ошибки. В таком случае важно сохранить проблемное сообщение, но при этом сделать так чтобы обработчик продолжил обрабатывать последующие сообщения. |
33 | |||
![]() |
5.3 | 34 | Возможен вариант, когда сообщение просто записывается в БД в статусе ожидает обработку. И фоновый обработчик разбирает таблицу и обрабатывает необходимые строки. |
![]() |
5.8 | 35 | )))|(% style="width:539px" %) |
![]() |
2.1 | 36 | |
![]() |
5.3 | 37 |