Как обеспечить рассылку уведомлений на большое количество пользователей в Go
Что проверяют
Видите ли вы за вопросом систему, а не цикл с горутинами. Рассылка на миллион адресов упирается не в Go, а во внешних провайдеров, их лимиты, повторы и дубли. Следом спрашивают, что будет, если процесс упал на середине, и как не отправить одно уведомление дважды.
Как отвечать
Разложите решение по слоям:
- постановка задач: рассылку не делают в HTTP-обработчике. Сервис создаёт задачу, режет аудиторию на пачки курсором по
user_id(не черезOFFSET) и кладёт сообщения в брокер: Kafka, RabbitMQ или NATS JetStream. Чтобы запись в базе и публикация не разошлись, применяют transactional outbox; - пул воркеров: фиксированное число горутин читает из консьюмера, а не отдельная горутина на каждого пользователя. Удобно через
errgroup.GroupсSetLimit(n)иcontext.Contextдля отмены и корректной остановки поSIGTERM; - ограничение скорости: у FCM, APNs, SMS-шлюзов и почты свои лимиты. На каждый канал доставки ставят
rate.Limiterизgolang.org/x/time/rateсWait(ctx)и используют пакетные API там, где они есть; - повторы: экспоненциальная задержка с джиттером на временных ошибках (429, 5xx) и никаких повторов на постоянных, например на недействительном токене устройства, который надо удалить из базы. Исчерпавшие попытки сообщения уходят в dead letter queue;
- идемпотентность: брокеры обычно гарантируют доставку «хотя бы один раз», поэтому нужен ключ из
campaign_idиuser_idи отметка об отправке под уникальным индексом; - наблюдаемость: счётчики отправленных и ошибок, лаг очереди в Prometheus.
Масштабируется это добавлением консьюмеров: партиции Kafka или конкурирующие потребители RabbitMQ распределяют работу сами. Отдельно проверяйте настройки пользователя: отписку и тихие часы.
Где спотыкаются
Запускают go send(u) на каждого пользователя. Миллион горутин рантайм переживёт, но провайдер ответит 429, а исходящих соединений не хватит.
Держат всю очередь в буферизированном канале в памяти. При рестарте пода рассылка теряется, и узнать, кому уже ушло, нельзя.
Забывают про дубли и отписки: повтор после таймаута шлёт второе сообщение, а пользователь, отключивший уведомления, всё равно их получает.