Асинхронные операции и Webhooks
Введение: очередь в кафе и номерок ☕🔔
Представь: ты заказал сложный торт. Его не делают за 5 секунд, поэтому тебе дают номерок.
Ты идёшь по своим делам, а когда торт готов — тебя зовут.
Асинхронные операции в API работают так же: запрос приняли, работа идёт в фоне.
💡 Совет: если действие занимает больше пары секунд — делай его асинхронным.
✅ Вывод: асинхронность снимает ожидание и снижает ошибки от повторов.
Проблема → решение
Проблема: долгий запрос зависает, пользователь жмёт «обновить» и создаёт дубликаты.
Решение: принять запрос, выдать Job ID и сообщить результат позже (polling или webhook).
Чем помогает и как работает
Асинхронная схема помогает не блокировать клиента и не перегружать сервер.
Как это работает: клиент отправляет запрос → сервер возвращает Job ID → работа выполняется в фоне → клиент получает результат по статусу или webhook.
Ключевые термины (простыми словами)
- Async (асинхронно) — результат приходит позже, а не сразу.
- Job (задача) — единица работы в фоне.
- Job ID — номер этой задачи.
- Queue (очередь) — список задач, которые ждут выполнения.
- Worker (воркер) — процесс, который выполняет задачи из очереди.
- Polling (пуллинг) — когда клиент сам периодически спрашивает статус.
- Webhook — когда сервер сам отправляет событие клиенту.
- Event ID — уникальный номер события для защиты от дублей.
- Signature (подпись) — защита webhook от подделки.
Самое важное (must‑know)
- ✅ Асинхронный старт обычно возвращает 202 Accepted и Job ID.
- ✅ Для клиента нужен endpoint статуса, где видно queued/processing/done/failed.
- ✅ Webhook нельзя принимать «как есть» — нужна подпись/секрет.
- ✅ Webhook может прийти дважды — нужен Event ID и дедупликация.
- ✅ Долгие операции нельзя «держать» одним запросом.
1. Асинхронная операция + Job ID
Назначение: принять запрос и запустить работу в фоне.
Простыми словами: сервер говорит «я начал, вот номер задачи».
Аналогия: ты оставил номер телефона в сервисе и ждёшь звонка.
Пример:
POST /reports202 AcceptedLocation: /jobs/abc123 { "jobId": "abc123"}🔎 Как это происходит на практике:
- Клиент запускает выгрузку отчёта.
- Сервер создаёт задачу и возвращает Job ID.
- Клиент показывает «готовится» и ждёт результат.
Характеристики:
- ✅ быстрый ответ (не ждём минуту);
- ✅ есть Job ID для отслеживания;
- ✅ работа идёт отдельно от запроса.
Когда использовать: отчёты, импорты, генерация файлов, массовые операции.
✅ Вывод: Job ID — это «номерок», который связывает клиента с задачей.
2. Статусы задачи (queued → processing → done/failed)
Назначение: показывать, на каком этапе находится работа.
Простыми словами: это короткое «в очереди / делаем / готово / ошибка».
Аналогия: табло в кафе: «принято», «готовится», «готово».
Пример:
GET /jobs/abc123200 OK { "status": "processing", "progress": 40}🔎 Как это происходит на практике:
- Клиент спрашивает статус по Job ID.
- Сервер отдаёт текущее состояние.
- UI обновляет «готовится 40%».
Характеристики:
- ✅ статусы фиксированы и документированы;
- ✅ есть причина ошибки при failed;
- ✅ прогресс опционален.
Когда использовать: всегда, если задача не мгновенная.
✅ Вывод: статус даёт пользователю понимание «что сейчас происходит».
3. Polling: периодическая проверка
Назначение: дать клиенту способ узнать результат без webhook.
Простыми словами: клиент сам «заглядывает» каждые N секунд.
Аналогия: ты проверяешь почтовый ящик, не ждёшь звонка.
Пример:
GET /jobs/abc123GET /jobs/abc123GET /jobs/abc123🔎 Как это происходит на практике:
- Клиент ставит таймер (например, раз в 5–10 секунд).
- Сервер отвечает текущим статусом.
- Клиент прекращает запросы, когда статус done/failed.
Характеристики:
- ✅ просто реализовать;
- ⚠️ больше нагрузки на сервер;
- ✅ работает, если webhook недоступен.
Когда использовать: простые проекты или когда клиент не принимает webhook.
✅ Вывод: polling — это проще, но дороже по нагрузке.
4. Webhook: сервер сам сообщает
Назначение: уведомлять клиента, когда всё готово.
Простыми словами: сервер сам стучится и говорит «готово».
Аналогия: курьер звонит, когда приехал.
Пример:
POST https://client.app/webhooks/report-readyContent-Type: application/json { "event": "report.ready", "jobId": "abc123"}🔎 Как это происходит на практике:
- Клиент заранее даёт URL для webhook.
- Сервер завершает работу и отправляет событие.
- Клиент получает событие и обновляет статус.
Характеристики:
- ✅ почти мгновенно;
- ✅ меньше запросов от клиента;
- ⚠️ нужен публичный endpoint.
Когда использовать: когда важна скорость и много задач.
✅ Вывод: webhook делает общение «сервер → клиент» быстрым и удобным.
5. Безопасность Webhook (подпись + Event ID)
Назначение: защитить webhook от подделки и дублей.
Простыми словами: «проверь пароль» и «не принимай одно и то же дважды».
Аналогия: курьер называет секретное слово и показывает номер заказа.
Пример:
POST /webhooks/report-readyX-Signature: sha256=8ab1...X-Event-Id: evt_123 { "event": "report.ready", "jobId": "abc123" }🔎 Как это происходит на практике:
- Сервер подписывает тело и ставит X-Signature.
- Клиент пересчитывает подпись своим секретом.
- Если подпись не совпала — запрос игнорируется.
Характеристики:
- ✅ подпись защищает от фейковых запросов;
- ✅ Event ID спасает от дублей;
- ✅ логика простая: «проверил → принял».
Когда использовать: всегда, если принимаете webhook извне.
✅ Вывод: подпись и Event ID — это минимальная безопасность и защита от повторов.
6. Очереди и воркеры (кто делает работу)
Назначение: выполнять тяжёлую работу отдельно от API.
Простыми словами: API только принимает заказ, а работу делают воркеры.
Аналогия: касса принимает заказ, кухня готовит.
Пример:
API -> Queue -> Worker -> обновление статуса🔎 Как это происходит на практике:
- API кладёт задачу в очередь.
- Воркер забирает задачу и выполняет.
- Результат записывается в статус или отправляется webhook.
Характеристики:
- ✅ снимает нагрузку с API;
- ✅ задачи выполняются последовательно;
- ✅ легко масштабировать воркеры.
Когда использовать: массовые операции, импорты, генерации.
✅ Вывод: очередь и воркер — это «двигатель» асинхронных задач.
Сравнение подходов
| Подход | Плюсы | Минусы | Когда подходит |
|---|---|---|---|
| Синхронно (в ответе) | простая логика | долгие ответы, таймауты | короткие операции |
| Асинхронно (Job ID) | нет ожидания, меньше таймаутов | нужна доп. логика | долгие операции |
| Способ получения результата | Плюсы | Минусы | Когда подходит |
|---|---|---|---|
| Polling | легко начать | много запросов | небольшая нагрузка |
| Webhook | быстро, меньше запросов | нужен публичный endpoint | большие системы |
Часто спрашивают на собеседованиях
- Почему для длинной операции возвращают 202, а не 200? ✅ Вывод: 202 означает «принято в работу», результат будет позже.
- Чем polling отличается от webhook? ✅ Вывод: polling — клиент спрашивает сам, webhook — сервер сообщает сам.
- Как защитить webhook? ✅ Вывод: проверять подпись и использовать секрет.
- Что делать, если webhook пришёл дважды? ✅ Вывод: хранить Event ID и игнорировать дубликаты.
- Как не создать дубли при повторном POST? ✅ Вывод: использовать Idempotency‑Key или проверять одинаковые запросы.
Типичные ошибки
Ошибка 1: держать долгий запрос до конца
❌ Неправильно: ответ приходит через минуту.
✅ Правильно: вернуть 202 и Job ID.
Почему: иначе таймауты и дубли.
Ошибка 2: нет endpoint статуса
❌ Неправильно: клиент не знает, готово ли.
✅ Правильно: GET /jobs/{id} со статусом.
Почему: пользователь должен видеть прогресс.
Ошибка 3: webhook без подписи
❌ Неправильно: принимать любые POST.
✅ Правильно: проверять X-Signature.
Почему: иначе любой может подделать событие.
Ошибка 4: polling каждую секунду
❌ Неправильно: спамить сервер.
✅ Правильно: разумный интервал и backoff.
Почему: это снижает нагрузку.
Ошибка 5: не обрабатывать дубликаты webhook
❌ Неправильно: каждое событие создаёт запись.
✅ Правильно: проверять Event ID.
Почему: webhooks часто ретраятся.
Ошибка 6: смешивать фильтры и job id
❌ Неправильно: /jobs?id=123
✅ Правильно: /jobs/123
Почему: Job ID — это ресурс, он в path.
Best Practices
- Возвращайте 202 + Job ID для долгих операций.
- Документируйте статусы и поля ответа.
- Ограничивайте частоту polling (5–10 сек).
- Используйте подпись и Event ID для webhook.
- Делайте обработку webhook быстрой (200 OK сразу).
- Логи и метрики по jobId помогают искать ошибки.
Заключение
Асинхронные операции спасают от таймаутов и дублей.
Webhook делает уведомления быстрыми и удобными.
✅ Вывод: если работа долгая — делай её асинхронной и сообщай результат правильно.