webhook-proxy
Промежуточный слой между нашими сервисами и вебхуками Discord. Он ставит события в очередь по цели, соблюдает лимиты 429 и повторяет ошибки 5xx, поэтому уведомления не теряются при внезапном трафике. Единая точка, через которую проходит вся исходящая коммуникация.
Единая точка выхода для всей исходящей коммуникации в Discord. Она ставит события в очередь по цели, уважает лимиты 429 с правильным backoff и повторяет ошибки 5xx вместо того, чтобы терять уведомления при всплеске трафика. Итог прост: событие не исчезает только из-за того, что случайно сработал rate limit.
Введение
Наши сервисы немало говорят в Discord: алармы мониторинга, уведомления о событиях, операционные логи. Каждое из них идёт через вебхук, а у вебхуков Discord жёсткие лимиты частоты. Пока трафик спокойный, этого никто не чувствует. Проблема начинается ровно тогда, когда уведомление нужнее всего - при всплеске, когда несколько сервисов шлют одновременно.
webhook-proxy появился, чтобы перестать лепить обработку лимитов в каждом сервисе по отдельности. Вместо этого вся исходящая коммуникация идёт через одну точку, которая берёт на себя доставку: ставит в очередь, ждёт когда надо, повторяет когда стоит и не теряет событие по пути. Для сервисов по ту сторону ничего не меняется - они шлют как раньше.
Проблема, которую видно только под нагрузкой
При спокойном трафике наивная отправка вебхука работает безупречно, и именно поэтому она коварна. Код стреляет запросом, получает 2xx, идёт дальше - и ни у кого нет повода подозревать, что что-то хрупко. Хрупок только путь ошибки, который при спокойном трафике почти никогда не срабатывает.
Достаточно всплеска, чтобы это вышло наружу. Несколько сервисов шлют уведомления одновременно, вы попадаете в лимит и получаете 429, либо Discord на мгновение отдаёт 5xx. В наивном подходе такое событие просто исчезает: код идёт дальше, исключение в лучшем случае попадает в лог, а в Discord не хватает записи, которая должна была там быть. Хуже всего то, что вы теряете его именно на пике трафика, то есть тогда, когда уведомления важнее всего.
Поэтому proxy - это не оптимизация производительности. Это гарантия доставки в условиях, где наивная отправка тихо подводит.
у каждого вебхука своя очередь, так что одна медленная цель не блокирует остальные.
при лимите proxy ждёт ровно столько, сколько говорит заголовок Retry-After, а не на глаз.
ошибка сервера означает экспоненциальный backoff и очередную попытку, а не потерю события.
неверный payload не станет верным с третьего раза, поэтому мы не тратим на него попытки.
Очередь, которая не блокирует остальных
Первое решение - отдельная очередь на каждую цель. Если бы все события шли через одну общую очередь, один медленный или заблокированный вебхук приостановил бы доставку ко всем остальным - одна забитая цель и тишина повсюду. Разделение очередей делает так, что проблема с одним вебхуком остаётся при этом одном вебхуке.
Та же структура даёт устойчивость к всплескам. Когда приходит волна, события не теряются - они попадают в очередь своей цели и ждут окна, в котором лимит отпускает. Снаружи это выглядит как временная задержка вместо потери, а это совсем другое качество: задержанное уведомление по-прежнему выполняет свою роль, потерянное не выполняет никакой.
Сердце: цикл доставки
Вся логика сводится к одному решению на ответ: успех завершает, 429 велит подождать, 5xx - повторить, прочие 4xx - конец без повтора. Различение этих случаев - это разница между proxy, который доставляет, и таким, который либо теряет события, либо бесконечно мелет неверный payload.
Асимметрия: 429 против 5xx
Ключевое - асимметрия между 429 и 5xx. При 429 Discord сам говорит, сколько ждать - мы читаем это из заголовка и ждём ровно столько, не меньше и не больше. При 5xx никто ничего не говорит, поэтому мы добавляем экспоненциальный backoff, чтобы не добивать временно упавший сервер серией немедленных попыток.
Самая частая ошибка - повторять всё подряд вслепую. На 429 повтор имеет смысл, но на 400 или 404 - нет: payload сам себя не починит, а вы лишь быстрее добираетесь до лимита и бесконечно мелете то, что никогда не пройдёт. Повторяйте то, что временное, и только это. Остальное должно упасть сразу, громко и один раз.
Эффект: всплеск, которого не видно по ту сторону
Для сервисов по ту сторону proxy невидим - они шлют ровно как раньше и ничего не знают о лимитах или повторах. Вся эта сложность сидит в одном месте, а не размазана по каждому сервису по отдельности. Это также значит, что правка в обработке лимитов идёт один раз, а не в несколько кодовых баз сразу.
Что proxy делает с ответом (иллюстративно)
Разница видна лишь при следующей волне трафика, и в этом весь смысл. Уведомления не исчезают тихо в момент, когда они нужнее всего - они ждут в очереди и доходят, как только лимит отпускает. Одна точка выхода превратила тихую потерю событий максимум во временную задержку, и в этом вся разница между мониторингом, которому можно доверять, и таким, который молчит именно при пожаре.
Больше проектов
Другие работы из той же категории - посмотрите, как мы решаем похожие задачи.
Есть похожий проект?
Напишите нам - смета бесплатна и приходит в течение часа.



