Очередь перед чужим API
Почему между вебхуком и внешней CRM всегда стоит очередь, и какие три вещи делают её надёжной.
Проблема
Облачная АТС присылает вебхук о звонке и ждёт ответ две секунды. CRM, куда этот звонок надо записать, отвечает до пяти секунд, а иногда отдаёт 500 без тела. Если ходить в CRM прямо из обработчика вебхука, часть звонков теряется: АТС не дождалась ответа, CRM упала, событие пропало.
Решение: принять быстро, обработать потом
Контроллер делает минимум: проверяет подпись и кладёт событие в очередь. Ответ уходит за миллисекунды, а медленная работа достаётся воркеру.
public function __invoke(Request $request): Response
{
$this->signature->verify($request);
$this->bus->publish('calls', CallEvent::fromWebhook($request->all()));
return response()->noContent();
}Три вещи, без которых очередь не спасает
Идемпотентность
АТС при нестабильной связи присылает один и тот же вебхук несколько раз. Ключ идемпотентности по call_id превращает повтор в пустую операцию.
if (! $this->processed->claim($event->callId)) {
return; // уже обработан
}Повтор с паузой
Если CRM ответила ошибкой, сообщение возвращается в очередь. Пауза растёт: 2, 4, 8 секунд. Три попытки, потом сообщение уходит в отдельную очередь для разбора руками.
Наблюдаемость
Каждое событие логируется с итогом: создана задача, создан лид, отложено. Без этого очередь превращается в чёрный ящик, где «что-то иногда теряется».
Что получилось
Так же устроены вебхуки телеметрии в Waviot, которые я довожу: сообщения приборов лежат в durable-очереди RabbitMQ, обработчик держит heartbeat, а сбой базы или брокера переживается повторами, а не потерей событий.