Всем привет! Я только начал изучать RabbitMq и многого не понимаю, поэтому прошу не пинать.
Допустим, у меня есть 100тыс пользователей, которым нужно отправить push уведомления, например, когда добавилась какое-то событие, ну пост добавился в конце концов (просто для примера).
И вот, в систему залетает пост, значит мне нужно добавить в очередь задачу, которая отправит 100тыс push сообщений пользователям. Но push сообщения можно отправлять только по 1тыс штук за раз. Значит мне нужно 100 раз выполнить одно и то же задание. И тут у меня проблема:
Если я отправлю в очередь 'send_push' сообщение '{post_id: 123}': , его получит консьюмер, выберет из БД 1тыс пользователей, сгенерирует push уведомление, отправит в Google Cloud Messages, сохранит где-то на диске последнего обработанного пользователя (чтобы с него начать отправлять следующую партию push) и вот все... На этом очередь очистится.
Как мне заставить очередь запускаться до тех пор, пока она не отправит push всем пользователям?
Например, кладите в очередь не одно сообщение, а сообщение на задание. То есть в сообщении будет post_id и список user_ids, в котором будет 1000 айдишников.
Получится 100 сообщений, будете их по одному разгребать.
Но получается, что при добавлении поста, я буду должен достать 100тыс записей из БД и сгенерировать из них 100 массивов отправив их в очередь.
По сути это довольно сложная операция, которая не должна выполняться во время создания поста.
Меня это натолкнуло на мысль, что можно ведь сделать так:
Отправляем задание в очередь {post_id: 123} - его берет консьюмер, получает 100к записей, создает 100 массивов с ID - отправляет в другую очередь, а там уже еще один консьюмер обрабатывает каждый массив и отправляет пуш.
Такое бывает?
Есть вероятность что консьюмер упадет во время работы. Если он не фиксирует, кому он уже сообщения отправил, или отправил пуши и упал зафиксировав это, то будут дубли пушей. Опять же, если фиксировать последнего отправленного, то нужно гарантировать порядок выдачи списка с пользователями на базе данный. Если user_id - гуид, например, то нужно будет вводить какой-то доп. идентификатор для этих целей.
Если решать эту проблему, используя утверждение "есть в очереди = пуш не отправлен", то не надо ничего сохранять. В очередь пишем пары post_id + массив user_id. В этом случае можно парралелить консьюмеров и никакой порядок не важен.
Роман Мирр, суть моего решения в том чтобы избавиться от этой необходимости. Есть что-то в очереди = нужно что-то отправить. Ничего нет в очереди = всё уже отправлено.
А если хочется хранить состояние отправки каждого пуша то надо не использовать очередь а взять базу данных и писать туда все что угодно.
Роман Мирр, нужно брать бд с поддержкой транзакций. Открыли транзакцию
Заблокировали строчку
Отправили пуш
Пометили отправленным в базе
Закрыли транзакцию
Если где-то посередине консьюмер упал, то транзакция откатится. Худшая ситуация - повторная отправка пуша. Но она и в варианте с очередью есть.
Но опять же, я не вижу смысла в хранении статуса здесь. Статистику по пушам может показать и сервис по отправке пушей.