Практика разработки

Как выбрать между polling, webhook и WebSocket: что на самом деле нужно для «реального времени»

Фраза:

Хотим, чтобы всё работало в реальном времени.

встречается в технических заданиях очень часто.

Например заказчик хочет, чтобы:

  • новые сообщения появлялись без обновления страницы;
  • статус заказа менялся сразу;
  • администратор моментально видел новую заявку;
  • платёж автоматически переходил в состояние «Оплачен»;
  • данные CRM синхронизировались без ручного обновления;
  • прогресс длительной операции отображался пользователю;
  • уведомления появлялись сразу после события.

На первый взгляд все эти задачи похожи.

Кажется, достаточно выбрать одну «real-time технологию» и использовать её везде.

Например:

WebSocket

потому что он звучит наиболее современно.

На практике это почти всегда неправильный подход.

У разных задач совершенно разная природа.

Для одной:

polling каждые 30 секунд

будет проще, дешевле и надёжнее.

Для другой действительно нужен:

WebSocket.

Для третьей WebSocket вообще не решает основную задачу, потому что событие приходит не от браузера пользователя, а от внешней системы.

Тогда нужен:

webhook.

Поэтому правильный вопрос звучит не:

Что лучше — polling, webhook или WebSocket?

А:

Кто кому должен сообщить об изменении, насколько быстро это должно произойти и насколько критично пропустить событие?

Разберём эти три подхода без маркетинговой магии вокруг слов «real time».


Сначала определим участников

В обычном веб-продукте могут участвовать:

Пользовательский браузер
        │
        ▼
Наш backend
        │
        ▼
Наша база данных

Но могут появляться и внешние сервисы:

Платёжная система
CRM
Служба доставки
Email provider
Банк
Сторонний API

Теперь направление обмена информацией может быть разным.

Например:

Browser
→ Backend

или:

Backend
→ Browser

или:

External service
→ Backend.

Именно направление события часто определяет технологию.


Самое простое правило

Можно начать с такой схемы:

Клиент периодически спрашивает сервер:
«Есть что-нибудь новое?»
→ polling

Внешний сервис сам сообщает нашему backend:
«У меня произошло событие»
→ webhook

Наш backend должен сразу отправлять события
подключённому браузеру:
«Вот изменение»
→ WebSocket

Уже этого достаточно, чтобы не пытаться решить все три задачи одной технологией.


Что такое polling

Polling — это самый простой вариант.

Клиент периодически делает запрос:

GET /api/orders/1842/status

и получает:

{
  "status": "processing"
}

Через несколько секунд снова:

GET /api/orders/1842/status

Теперь:

{
  "status": "completed"
}

То есть никто специально не отправлял клиенту событие.

Клиент сам регулярно спрашивал:

Что-нибудь изменилось?

Это похоже на проверку почтового ящика

Каждые пять минут человек подходит:

Есть письмо?
Нет.

Есть письмо?
Нет.

Есть письмо?
Да.

Это и есть polling.


Почему polling часто считают плохим решением

Потому что он делает лишние запросы.

Например:

1000 пользователей

проверяют статус каждые:

5 секунд.

Получаем:

12 запросов в минуту
×
1000
=
12 000 запросов в минуту.

При этом большинство ответов могут быть:

ничего нового.

Кажется неэффективным.

И действительно, для некоторых продуктов это плохая архитектура.

Но не для всех.


Polling имеет огромное преимущество — простоту

Не нужны:

постоянные соединения;
WebSocket gateway;
reconnect logic;
connection registry;
heartbeat;
message fan-out.

Обычный HTTP.

Обычная авторизация.

Обычные backend endpoints.

Обычный reverse proxy.


Поэтому polling отлично подходит, если задержка допустима

Например пользователь отправил документ на генерацию.

Процесс занимает:

2–5 минут.

Frontend показывает:

Файл формируется...

и проверяет:

GET /api/jobs/123

раз в:

5 секунд.

Нужен ли здесь WebSocket?

Чаще всего нет.

Разница между:

результат появился в 12:00:01

и:

пользователь увидел его в 12:00:05

никакого бизнес-значения не имеет.


Ещё пример — статус заказа

Заказ:

Принят
↓
Комплектуется
↓
Передан в доставку
↓
Доставлен

Если изменение происходит раз в несколько часов, WebSocket ради доставки статуса на три секунды быстрее будет архитектурным излишеством.

Polling раз в:

30–60 секунд

может быть более чем достаточным.


Polling особенно хорош для административных интерфейсов

Например dashboard показывает:

новые заявки;
очередь задач;
статус backup;
метрики;
состояние интеграций.

Если информация обновляется раз в:

15–30 секунд,

пользователь всё равно воспринимает интерфейс как практически живой.


Real time не всегда означает миллисекунды

Для разных задач «быстро» означает разное.

Например:

Чат:
100–500 мс желательно.

Новая заявка админу:
1–5 секунд отлично.

Изменение статуса заказа:
10–30 секунд нормально.

Построение отчёта:
несколько секунд задержки неважно.

Обновление статистики:
30–60 секунд может быть достаточно.

Поэтому требование:

Нужно real time.

нужно переводить в конкретное:

Какую максимальную задержку пользователь готов заметить?

Polling бывает адаптивным

Не обязательно спрашивать сервер каждые две секунды вечно.

Например:

первые 30 секунд:
каждые 2 секунды

потом:
каждые 5 секунд

через минуту:
каждые 15 секунд

Это снижает нагрузку.


Можно останавливать polling

Например job перешёл:

RUNNING
→ COMPLETED.

После этого:

polling stop.

Можно прекращать polling, если вкладка неактивна

Browser Page Visibility API позволяет понимать:

пользователь вообще смотрит страницу?

Если нет:

interval ↑

или запросы временно останавливаются.


Таким образом polling не обязательно примитивен

Он может быть:

адаптивным;
экономным;
условным;
зависящим от состояния.

Когда polling становится плохим

Например чат.

Пользователь пишет:

Привет.

Второй пользователь должен увидеть сообщение практически сразу.

Если polling раз в:

30 секунд,

чат ощущается сломанным.

Если раз в:

1 секунду,

на:

10 000 пользователей

создаётся огромный поток одинаковых HTTP-запросов.

Вот здесь появляется смысл WebSocket.


Что такое WebSocket

Обычный HTTP работает примерно так:

Client
→ Request
→ Server

Server
→ Response

Connection ends

Для следующего обновления нужен новый request.


WebSocket строит другое взаимодействие.

Клиент устанавливает соединение:

Browser
⇄
Server

и сохраняет его.

Теперь обе стороны могут отправлять данные в любой момент.


Например чат

Пользователь A пишет:

Привет

Backend сохраняет сообщение.

И сразу отправляет через WebSocket:

new_message

пользователю B.

Не нужно ждать следующего:

GET /messages.

Поэтому WebSocket подходит для настоящего интерактивного real time

Например:

чат;
онлайн-игра;
совместное редактирование;
торговый терминал;
живой мониторинг;
presence;
оперативные уведомления;
диспетчерские панели.

Но WebSocket — это не просто «HTTP быстрее»

С ним появляется новый класс инфраструктуры.

Система должна понимать:

кто сейчас подключён;
какому пользователю принадлежит connection;
к какому проекту он имеет доступ;
что делать после reconnect;
как обнаружить мёртвое соединение;
как масштабировать несколько серверов.

Представим один backend

User A
   │
   ▼
Server 1

и:

User B
   │
   ▼
Server 1

Server 1 легко знает оба connection.


Теперь backend масштабировали

User A
   │
   ▼
Server 1

User B
   │
   ▼
Server 2

Пользователь A отправляет сообщение на:

Server 1.

Но connection пользователя B находится:

на Server 2.

Как Server 1 доставит ему событие?


Теперь нужен межсерверный слой

Например:

Redis Pub/Sub;
Redis Streams;
NATS;
RabbitMQ;
другой message broker.

Схема:

User A
↓
Server 1
↓
Pub/Sub
↓
Server 2
↓
User B

То есть простое требование:

Сделайте всё в реальном времени.

может заметно изменить backend architecture.


WebSocket требует reconnect

Мобильный интернет пропал.

Wi-Fi сменился.

Ноутбук проснулся после сна.

Nginx перезапущен.

Deploy заменил container.

WebSocket разорван.

Клиент должен:

обнаружить disconnect;
подождать;
переподключиться;
повторно авторизоваться;
восстановить подписки.

А что произошло, пока соединения не было?

Очень важный вопрос.

Например:

12:00 connection lost

12:01 новое сообщение

12:02 новое сообщение

12:03 reconnect

Если система просто снова открыла socket, пользователь может навсегда потерять два события.


Поэтому WebSocket не отменяет обычный API

Хорошая архитектура часто выглядит так:

REST/HTTP
=
получить текущее состояние

WebSocket
=
сообщить, что состояние изменилось

После reconnect клиент может сделать

GET /api/messages?after=lastKnownId

и восстановить пропущенные данные.


Это очень важный принцип

WebSocket — канал доставки событий, а не обязательно источник истины.

Источник истины остаётся:

database/API.

WebSocket особенно легко переоценить

Например заказчик говорит:

Хочу, чтобы статус оплаты менялся моментально. Значит нужен WebSocket.

Но откуда наш backend вообще узнает, что платёж прошёл?

WebSocket между:

Browser
⇄
Наш backend

этого не решает.

Платёж происходит в:

внешней платёжной системе.

Именно здесь нужен webhook.


Что такое webhook

Webhook — это ситуация, когда один сервер сообщает другому серверу о событии.

Например:

Платёжная система
↓
POST /webhooks/payment
↓
Наш backend

Payload:

{
  "type": "payment.succeeded",
  "paymentId": "pay_123"
}

Теперь backend знает:

Платёж состоялся.

Webhook — не технология обновления браузера

Это очень важное различие.

Webhook работает:

Server
→
Server.

WebSocket чаще используется:

Server
⇄
Client.

Поэтому одна реальная функция может использовать сразу обе технологии

Например пользователь оплачивает заказ.

Полный путь:

Пользователь
↓
Платёжная система
↓
платёж выполнен
↓
Webhook
↓
Наш backend
↓
Payment = PAID
↓
WebSocket
↓
Browser
↓
«Оплата получена»

Вот здесь:

webhook

и:

WebSocket

не конкуренты.

Они решают разные части одной задачи.


Можно заменить WebSocket polling'ом

Тот же процесс:

Платёж
↓
Webhook
↓
Backend
↓
Payment = PAID

а frontend каждые три секунды:

GET /api/payment/status

Через несколько секунд видит:

PAID.

Какая архитектура лучше?

Зависит от продукта.

Если после оплаты пользователь может подождать:

2–3 секунды,

polling проще.

Если интерфейс должен реагировать практически мгновенно:

WebSocket

может быть оправдан.

Webhook при этом нужен в обоих случаях.


Это одна из главных ошибок в требованиях

Заказчик говорит:

Нам нужен WebSocket для интеграции с CRM.

Но если CRM должна сама сообщать:

Лид изменился,

правильный механизм, возможно:

CRM webhook
→ наш backend.

WebSocket здесь вообще не участвует.


Или наоборот

CRM не поддерживает webhook.

Есть только:

GET /crm/leads

Тогда никакой WebSocket на нашей стороне не заставит CRM внезапно отправлять события.

Придётся использовать polling внешнего API.


Получается интересная комбинация

CRM
↑
polling
│
наш backend
↓
WebSocket
│
Browser

Backend каждые:

30 секунд

проверяет CRM.

Когда находит изменение, моментально отправляет его открытой админ-панели через WebSocket.

Для пользователя интерфейс выглядит:

real time.

Хотя источник данных обновляется polling'ом.


Это важный вывод

Real-time UX и real-time source — не одно и то же.

Интерфейс может выглядеть мгновенным, даже если одна часть системы обновляется с небольшой задержкой.


Что значит «всё в реальном времени» для бизнеса

Лучше разбить требование на события.

Например:

СобытиеДопустимая задержка
Сообщение в чате< 1 сек
Новая заявка админу1–5 сек
Статус оплаты1–5 сек
Статус доставки30–60 сек
Обновление dashboard15–30 сек
Финансовый отчётминуты

После этого выбор технологии часто становится очевиден.


Пример: чат

Требования:

сообщения сразу;
typing indicator;
online status.

Хороший кандидат:

WebSocket.

Почему?

Событий много.

Задержка заметна.

Связь двухсторонняя.


Можно ли использовать webhook?

Нет в основной части.

Оба пользователя находятся внутри нашего продукта.

Нет внешнего сервера, которому нужно уведомить наш backend.


Можно ли polling?

Технически:

да.

Но UX при масштабе и высокой частоте будет хуже.


Пример: платёж

Требование:

Как только банк подтвердил платёж, заказ должен стать оплаченным.

Главный механизм:

payment provider
→ webhook
→ backend.

Нельзя полагаться только на browser redirect

Например после оплаты provider отправляет пользователя:

/success.

Это ещё не надёжное подтверждение оплаты.

Пользователь может:

закрыть вкладку;
потерять интернет;
подделать URL.

Backend должен получить доверенное состояние через:

webhook

или дополнительный API reconciliation.


А как обновить экран пользователя?

Можно:

polling

или:

WebSocket.

Это отдельное решение.


Пример: новая заявка админу

Пользователь отправил форму.

Backend уже знает о новой заявке.

Нужно показать админу:

Новая заявка #1842.

Варианты:

Polling

Админ-панель каждые:

10 секунд

проверяет новые заявки.

Для небольшого проекта этого вполне достаточно.


WebSocket

Backend сразу:

push new_lead

в открытый dashboard.

Если скорость действительно важна — хороший вариант.


Webhook здесь не нужен

Потому что событие уже возникло внутри нашего backend.


Пример: синхронизация со службой доставки

Нужно знать статус:

Принято
В пути
Доставлено

Если служба доставки предоставляет webhook:

используем webhook.

Если не предоставляет:

polling API.

WebSocket внутри нашего сайта не решает получение внешнего статуса.


Пример: прогресс генерации отчёта

Пользователь нажал:

Сформировать PDF.

Backend создаёт background job.

Процесс:

0%
↓
30%
↓
70%
↓
100%.

Нужен ли WebSocket?

Если отчёт строится:

20 секунд

и хочется красивый живой progress:

WebSocket

или Server-Sent Events могут быть хорошим решением.


Но если задача обычно занимает

3 секунды,

проще показать spinner.


Если процесс занимает пять минут

Polling каждые:

5 секунд

может быть проще и абсолютно достаточен.


Не каждое percentage update имеет реальную ценность

Иногда система показывает:

43%
44%
45%

но backend на самом деле не знает точный progress.

Разработчик просто придумал визуальную анимацию.

Тогда WebSocket никак не делает систему честнее.


Polling: преимущества

простая реализация;
обычный HTTP;
простая авторизация;
легко кешировать;
легко логировать;
хорошо работает через proxies;
простое горизонтальное масштабирование;
простой recovery после offline.

Polling: недостатки

лишние запросы;
задержка зависит от interval;
при слишком частом polling растёт нагрузка;
неидеален для очень частых событий.

Webhook: преимущества

внешняя система сообщает сама;
нет постоянного опроса;
событие может прийти быстро;
хорош для server-to-server интеграций.

Webhook: недостатки

публичный endpoint;
нужна проверка подписи;
дубли;
retry;
replay protection;
out-of-order события;
нужно durable processing.

Webhook — не просто:

POST + JSON.

Это отдельный интеграционный протокол.


WebSocket: преимущества

низкая задержка;
двусторонняя связь;
удобен для частых событий;
сильный real-time UX.

WebSocket: недостатки

постоянные соединения;
reconnect;
heartbeat;
масштабирование;
connection state;
пропущенные события;
fan-out между серверами;
более сложное тестирование.

А что такое Server-Sent Events?

Есть ещё промежуточный вариант:

SSE

или Server-Sent Events.

Он часто остаётся за рамками разговора:

Polling или WebSocket?

Хотя для некоторых задач подходит отлично.


SSE работает только в направлении

Server
→
Browser.

Клиент открывает HTTP connection.

Server постепенно отправляет события.


Например

progress;
notifications;
logs;
status updates.

Если браузеру не нужно отправлять множество сообщений обратно по тому же каналу, SSE может быть проще WebSocket.


Чат

Нужна двусторонняя активная связь:

WebSocket.

Живой progress build

Нужно только:

Server → Browser.

Можно рассмотреть:

SSE.

Но даже SSE не нужно выбирать автоматически

Если update раз в:

30 секунд,

polling всё равно может быть проще.


Поэтому реальный выбор чаще выглядит так

Polling
Webhook
SSE
WebSocket

а не только:

WebSocket = современно.

Почему «давайте WebSocket везде» обычно плохая идея

Потому что WebSocket решает одну конкретную проблему:

частая низколатентная связь между уже подключёнными сторонами.

Он не решает автоматически:

надёжную бизнес-доставку;
долговременное хранение событий;
восстановление истории;
синхронизацию с внешним provider;
точность состояния после disconnect.

Представим уведомления

Backend отправил:

new_notification

через WebSocket.

В этот момент пользователь был offline.

Уведомление потеряно?

Если да — архитектура ненадёжна.


Значит само уведомление должно храниться

Например:

notifications table

содержит:

id
user_id
message
created_at
read_at

WebSocket только сообщает:

Появилась новая запись.

После reconnect frontend делает

GET /api/notifications?after=...

и получает пропущенные.


То есть правильная модель

Database
=
истина

WebSocket
=
ускоритель доставки

Это очень важный принцип для заказчика

Когда звучит:

Мне нужно, чтобы уведомления не терялись.

ответ:

«Нужен WebSocket»

недостаточен.

Нужна:

durable notification storage.

То же относится к чату

Сообщение должно сначала стать:

durable data.

Например:

INSERT messages
COMMIT

а потом:

WebSocket broadcast.

Не наоборот

Плохо:

WebSocket send
↓
DB insert
↓
database error.

Получатель увидел сообщение, которого официально не существует.


Хорошая схема

Sender
↓
POST /messages
↓
DB transaction
↓
COMMIT
↓
WebSocket event
↓
Recipients

Если broadcast потерялся, сообщение всё равно можно восстановить через API.


WebSocket и background jobs

Представим worker выполняет:

импорт 100 000 строк.

Frontend хочет progress.

Worker не должен напрямую знать WebSocket connection пользователя.


Лучше

Worker
↓
updates job progress
↓
event bus
↓
WebSocket layer
↓
Browser

или frontend периодически получает progress через polling.


Это сохраняет архитектурные границы

Worker занимается:

работой.

Realtime gateway:

доставкой UI events.

Что делать после reconnect WebSocket

Никогда не предполагать:

Если socket снова открылся, состояние актуально.

Лучше иметь:

lastEventId

или:

lastMessageId

и после восстановления сделать:

sync.

До разрыва клиент видел:

message_id = 1830.

После reconnect:

GET /messages?after=1830

Backend возвращает:

1831
1832
1833.

Теперь client догнал состояние.


Для некоторых систем события можно replay

Например server хранит короткий event stream.

Клиент сообщает:

last sequence = 945.

Server продолжает:

946...

Но это уже более сложная архитектура.


Не нужно строить event streaming, если продукту достаточно GET после reconnect

Сложность должна соответствовать бизнесу.


Как выбирать interval для polling

Нет универсального:

5 секунд.

Сначала определить бизнес-задержку.


Пользователь ждёт оплату:

2–3 секунды.

Можно polling:

2 секунды.

Но только пока открыт экран оплаты.


Dashboard

15–30 секунд.

Статус доставки

60 секунд

или даже реже.


Долгий экспорт

Первые секунды:

2 sec

потом:

5 sec

затем:

10 sec.

Так polling становится значительно дешевле


Long polling

Есть ещё вариант между polling и WebSocket.

Client отправляет:

GET /events

Server не отвечает сразу.

Он держит request некоторое время.

Если появляется событие:

ответ.

Client сразу открывает следующий request.

Это:

long polling.

Сейчас его используют реже в новых browser systems

Потому что есть:

SSE;
WebSocket.

Но как концепция он показывает важную вещь:

между обычным polling и постоянным socket существует несколько компромиссов.


Что выбрать небольшому продукту

Очень часто:

обычный HTTP
+
polling
+
webhooks внешних providers

закрывают почти все задачи.


Не нужно начинать проект с

WebSocket cluster
Redis Pub/Sub
message broker

только потому, что в будущем, возможно, появится чат.


Сначала реальная функция

Если чат появился:

добавляем WebSocket там,
где он действительно нужен.

Можно одновременно использовать все три технологии

И это нормально.

Например SaaS:

статус background export.

Webhook

платёжная система;
CRM;
email provider.
чат клиента с менеджером;
живые уведомления.

Это не архитектурный хаос

Это правильное использование инструментов по назначению.


Пример клиентского кабинета

Допустим заказчик хочет:

После сообщения менеджера клиент должен сразу увидеть новое сообщение.

WebSocket:

подходит.

После оплаты счёта статус должен сразу стать «Оплачено».

Webhook:

provider → backend.

Плюс WebSocket или polling:

backend → client UI.

Статус длительной сборки архива должен обновляться.

Polling раз в:

3–5 секунд

может быть достаточно.


Получается гибрид

Payment provider
       │
       │ webhook
       ▼
    Backend
       │
       ├── DB
       │
       ├── WebSocket → browser
       │
       └── HTTP API ← polling

Не нужно искать один протокол для всей системы.


Что происходит при масштабировании WebSocket

На одном server всё просто.

При нескольких instances:

Load balancer
   │
   ├── App 1
   ├── App 2
   └── App 3

WebSocket connection пользователя находится только на одном instance.


Событие может родиться на другом

Поэтому нужен механизм:

publish event
↓
all app instances / gateway
↓
нужный connection.

Часто для этого появляется Redis

Например:

App 1
↓
Redis Pub/Sub
↓
App 3
↓
User.

Но это не означает, что Redis нужен каждому приложению с WebSocket

При одном instance или небольшой архитектуре можно начать проще.

Важно понимать момент, когда появится необходимость.


WebSocket и sticky sessions

Иногда load balancer закрепляет пользователя за одним server.

Это может упростить connection handling.

Но не решает полностью:

как событие с App 2
попадёт пользователю на App 1.

То есть distributed fan-out всё равно остаётся архитектурным вопросом.


WebSocket и авторизация

Нельзя просто открыть:

/ws

и доверять client-provided:

projectId.

Например пользователь подписывается:

project:1842.

Backend обязан проверить:

имеет ли этот пользователь
доступ к project 1842?

Иначе получается realtime IDOR

Пользователь меняет:

1842 → 1843

и начинает получать чужие события.


Поэтому WebSocket authorization должна использовать те же правила, что HTTP API

Не отдельную упрощённую безопасность.


То же касается комнат

Например:

room = project:1842

должна создаваться сервером на основании реальных permissions.

Не просто:

join whatever room client requested.

Webhook security совсем другая

Здесь нет пользовательской session.

Нужно проверить:

кто внешний sender?

Обычно используются:

HMAC signature;
timestamp;
secret;
event ID.

Поэтому даже security model у технологий разная

Polling/WebSocket

user authentication
+
authorization.
provider authentication
+
signature verification
+
replay protection.

Как объяснить заказчику стоимость

Требование:

Пусть всё обновляется мгновенно.

может казаться одной небольшой функцией.

Но WebSocket-подсистема может добавить:

connection manager;
reconnect;
heartbeat;
message routing;
authorization;
Redis/broker;
monitoring;
load tests;
fallback sync.

То есть стоимость разработки и эксплуатации вырастает.


Поэтому нужно спрашивать

Что бизнес потеряет, если обновление появится не через 200 мс, а через 5 секунд?

Если ответ:

Ничего.

Возможно, WebSocket не нужен.


А если ответ

Пользователь переписывается с оператором и пять секунд воспринимаются как тормоза.

Тогда real-time transport имеет реальную ценность.


Real time должно окупать сложность

Это, пожалуй, главный бизнес-принцип.


Нельзя выбирать технологию по моде

WebSocket

не лучше polling сам по себе.

Так же как:

микросервисы

не лучше монолита сами по себе.

Всё зависит от задачи.


Простая матрица выбора

ЗадачаPollingWebhookWebSocket
Проверка статуса background jobОтличноНетИногда
Получение события от платёжной системыНет/дополнениеОтличноНет
Чат пользователейПлохо на масштабеНетОтлично
Обновление dashboard раз в 30 секОтличноНетИзбыточно
Синхронизация с CRM, если CRM умеет callbackНетОтличноНет
CRM без callback APIОтличноНевозможноНе решает источник
Живые уведомления в браузереИногдаНетОтлично
Статус доставкиОтличноОтлично, если provider поддерживаетОбычно не источник
Совместное редактированиеПлохоНетОтлично
Платёж + мгновенное обновление интерфейсаPolling UIWebhook providerWebSocket UI

Как принимать решение по шагам

Вопрос 1. Где происходит событие?

Если внутри нашего backend:

WebSocket/SSE/polling.

Если во внешней системе:

webhook

если она его предоставляет.


Вопрос 2. Может ли внешний сервис сам нас уведомить?

Да:

webhook.

Нет:

polling external API.

WebSocket нашего браузера здесь ничего не изменит.


Вопрос 3. Насколько критична задержка?

до минуты
→ polling

несколько секунд
→ polling часто достаточно

менее секунды
→ WebSocket/SSE кандидат

Это не жёсткие правила, а хорошая отправная точка.


Вопрос 4. Односторонняя или двусторонняя связь?

Только:

Server → Browser

можно рассмотреть:

SSE.

Активно:

Browser ⇄ Server

WebSocket выглядит естественнее.


Вопрос 5. Что происходит offline?

Если ответ:

Событие просто потеряется.

архитектура почти наверняка неполная.

Нужен:

database;
event log;
API resync;

или другой durable source of truth.


Вопрос 6. Насколько часто происходят события?

раз в 10 минут

— WebSocket, возможно, не нужен.

20 событий в секунду

— polling, возможно, уже плохой выбор.


Вопрос 7. Сколько одновременно подключённых пользователей?

50

и:

500 000

— совершенно разные требования к WebSocket-инфраструктуре.


Что часто оказывается лучшим решением

Не:

выбрать одну технологию.

А:

выбрать минимально сложную
для каждого отдельного потока данных.

Чат
→ WebSocket

Платёж
→ webhook

Отчёт
→ polling

Server notification stream
→ SSE

Это называется не «разнобой»

Это нормальная архитектурная декомпозиция.


Важный сценарий: WebSocket disconnected, webhook пришёл

Представим пользователь находится на странице оплаты.

Socket разорвался.

В это время provider прислал:

payment.succeeded.

Backend правильно сохранил:

PAID.

Но browser не получил realtime event.


После reconnect интерфейс не должен оставаться в старом состоянии

Он делает:

GET /payment/current

и получает:

PAID.

Именно поэтому HTTP API остаётся обязательным

Realtime transport ускоряет UX.

Не заменяет способность восстановить текущее состояние.


Ещё один сценарий: событие WebSocket пришло два раза

Например после reconnect/retry.

Frontend должен по возможности уметь обрабатывать:

duplicate notification.

Например событие

{
  "type": "message.created",
  "messageId": 1842
}

пришло дважды.

Если UI использует:

messageId

как identity, второй event не создаёт второй message bubble.


То есть идемпотентность полезна даже в realtime UI

Не только в webhook.


Event payload лучше делать маленьким

Например WebSocket отправляет:

{
  "type": "project.updated",
  "projectId": 1842,
  "revision": 17
}

Frontend при необходимости обновляет объект.


Не обязательно отправлять огромный проект целиком

Особенно если:

project payload = 500 KB.

При частых событиях это создаёт лишний network traffic.


Но дополнительный GET тоже имеет стоимость

Поэтому нужно выбирать между:

event = notification only

и:

event = enough data to update UI.

Это уже зависит от продукта.


Хорошо иметь event version/revision

Например frontend уже знает:

revision 18.

Приходит:

revision 17.

Можно игнорировать старое событие.


Это защищает от out-of-order даже внутри realtime layer

Потому что WebSocket одного соединения упорядочен на транспортном уровне, но данные могут приходить из:

нескольких backend processes;
workers;
reconnect;
разных источников.

Domain revision всё равно полезна.


Что мониторить у WebSocket

Не только:

server is running.

Полезны:

active_connections;
connections_opened;
connections_closed;
reconnect_rate;
messages_sent;
delivery_errors;
auth_failures.

И heartbeat

Connection может технически существовать, но peer уже исчез.

Поэтому системы используют:

ping/pong

или application heartbeat.


Но heartbeat не является бизнес-событием

Не нужно записывать:

каждый ping

в основную БД.

Это эксплуатационный механизм.


Что мониторить у polling

Например:

requests/min;
unchanged response ratio;
client polling interval;
endpoint latency.

Если:

99.9%

ответов:

ничего не изменилось,

возможно interval слишком агрессивный.


Что мониторить у webhook

received;
duplicates;
signature failures;
processing failures;
oldest pending;
delivery lag.

Каждая технология имеет собственную эксплуатационную модель.


Не забывайте мобильные устройства

WebSocket на mobile web/PWA может разрываться при:

background;
sleep;
network change;
OS restrictions.

Поэтому:

reconnect + resync

обязательны.


А если нужно уведомить пользователя, когда приложение закрыто?

WebSocket не поможет.

Закрытого приложения уже нет online.

Нужны:

Web Push;
FCM;
APNs;
native push notifications.

Это ещё один важный пример

Заказчик говорит:

Нужно real time уведомление на телефон.

Разработчик отвечает:

Сделаем WebSocket.

Но если приложение закрыто:

socket отсутствует.

Нужен push notification.


То есть real-time архитектура может включать

WebSocket
для online user

Push
для offline user

Database
для durable notification history.

Один продукт — несколько delivery channels

Это нормально.


Можно ли сделать fallback

Да.

Например приложение сначала пытается:

WebSocket.

Если соединение недоступно:

polling every 15 sec.

Это повышает устойчивость.


Но fallback тоже нужно тестировать

Иначе он существует только на диаграмме.


Что выбрать для MVP

Очень часто:

polling.

Почему?

Потому что позволяет быстро проверить продуктовую ценность без лишней realtime infrastructure.


Например новая система заявок

На старте:

админ dashboard
polling каждые 10 sec.

Если через полгода оказывается, что администраторы реально постоянно работают в интерфейсе и мгновенные события повышают эффективность:

добавляем WebSocket.

Это безопаснее, чем заранее строить сложную realtime-платформу без пользователей


Но чат — исключение

Если core feature продукта:

переписка в реальном времени,

polling MVP может сразу создать неверное впечатление о качестве.

Там WebSocket имеет смысл с начала.


То есть MVP не означает «всегда polling»

МVP означает:

самая простая архитектура, которая всё ещё корректно проверяет ключевую ценность продукта.


Как переводить требование заказчика в техническое решение

Заказчик:

Хочу, чтобы всё было сразу.

Плохой ответ:

Тогда WebSocket.

Хороший диалог

Нужно спросить:

Что именно должно обновляться?

Например:

Новые сообщения.

Какая допустимая задержка?
Меньше секунды.

Кто создаёт событие?
Другой пользователь нашего приложения.

Теперь:

WebSocket

очень вероятно правильный выбор.


Другой пример

Нужно моментально видеть подтверждение платежа.

Спрашиваем:

Кто создаёт событие?
ЮKassa/банк/payment provider.

Тогда:

provider → webhook → backend.

И уже отдельно:

backend → browser

через:

polling или WebSocket.

Ещё пример

Хочу видеть изменение статуса груза сразу.

Provider:

не поддерживает webhook;
API обновляет статус каждые 10 минут.

Можно построить сколько угодно WebSocket.

Данные всё равно не станут свежее десятиминутного source.


Это нужно честно объяснять заказчику

Скорость интерфейса не может быть выше скорости источника данных.

Если source обновляется:

раз в 15 минут,

WebSocket способен мгновенно сообщить только:

старые данные.

Поэтому SLA real time нужно определять end-to-end

Не только:

WebSocket latency = 50 ms.

А:

событие произошло
↓
источник его зафиксировал
↓
мы узнали
↓
обработали
↓
отправили пользователю
↓
UI обновился

Например платёж

Provider processing:
700 ms

Webhook delivery:
300 ms

Backend processing:
100 ms

WebSocket:
50 ms

UI:
20 ms

Итого:

~1.17 sec.

Вот это реальная latency функции.


Если webhook provider доставляет событие через 30 секунд

WebSocket:

50 ms

не спасёт.


«Реальное время» нужно измерять от бизнес-события

Не от последнего backend hop.


Полезно определить SLA

Например:

95% новых сообщений
видны получателю < 1 sec.

или:

95% изменений статуса заказа
появляются < 30 sec.

Теперь есть инженерная цель.


И её можно тестировать

Не:

Кажется быстро.

А:

event_created_at
→
ui_received_at.

Стоимость ошибки тоже важна

Если dashboard опоздал на 20 секунд:

неприятно.

Если потерян:

payment.succeeded

это уже финансовый инцидент.


Поэтому webhook обычно требует более серьёзной надёжности, чем UI polling

Например:

подпись;
replay protection;
inbox;
dedupe;
reconciliation.

Потому что речь может идти о business truth.


WebSocket event можно иногда потерять

Если после reconnect client делает full sync.


То есть надёжность каналов тоже различается

Webhook:
business event delivery

WebSocket:
low-latency UI notification

Polling:
state refresh

Именно поэтому они часто работают вместе.


Архитектура сложного сценария

Например заказчик оплатил проект.

           Payment provider
                  │
                webhook
                  ▼
              Backend
                  │
          ┌───────┴────────┐
          ▼                ▼
      Database         WebSocket
          │                │
          │                ▼
          │             Browser
          │
          └── REST API ← polling/resync

Webhook сообщает backend.

Database фиксирует истину.

WebSocket ускоряет UI.

REST API позволяет восстановить состояние.

Каждый инструмент делает свою работу.


Как не усложнить систему

Перед добавлением WebSocket полезно спросить:

  1. Нельзя ли решить задачу polling'ом раз в несколько секунд?
  2. Насколько пользователю заметна такая задержка?
  3. Сколько событий действительно происходит?
  4. Есть ли одновременно подключённые пользователи?
  5. Нужна ли двусторонняя realtime-связь?
  6. Как восстанавливать пропущенные события?
  7. Что произойдёт после restart server?
  8. Как масштабировать несколько instances?
  9. Нужны ли offline notifications?
  10. Есть ли бизнес-выгода от дополнительной сложности?

Если половина вопросов не имеет смысла для задачи, возможно WebSocket избыточен.


Перед webhook другие вопросы

  1. Поддерживает ли provider webhook?
  2. Как проверяется подпись?
  3. Может ли событие прийти дважды?
  4. Каков retry policy?
  5. Гарантируется ли порядок?
  6. Есть ли event ID?
  7. Можно ли получить текущее состояние через API?
  8. Что делать при пропущенном событии?
  9. Как долго хранить dedupe history?
  10. Что считать успешным приёмом?

Перед polling

  1. Какой interval действительно нужен?
  2. Можно ли его увеличивать со временем?
  3. Можно ли прекращать polling после terminal state?
  4. Нужно ли poll при скрытой вкладке?
  5. Сколько будет одновременных клиентов?
  6. Насколько дорог endpoint?
  7. Можно ли возвращать только изменения?

Тогда выбор становится инженерным

Не:

Что моднее?

А:

Какой механизм минимально сложен и удовлетворяет требованиям?

Понятная формула для заказчика

Можно объяснить так:

«Мы периодически спрашиваем сервер, не изменилось ли что-нибудь».

Просто.

Надёжно.

Есть небольшая задержка.


«Внешняя система сама сообщает нашему серверу, когда у неё что-то произошло».

Хорошо для:

платежей;
CRM;
доставки;
внешних сервисов.

«Браузер держит постоянное соединение с нашим сервером, поэтому изменения можно отправлять пользователю практически мгновенно».

Хорошо для:

чатов;
живых уведомлений;
совместной работы;
интерактивных панелей.

И очень важное дополнение

Иногда для одной функции нужны сразу два или три подхода.

Например платёж:

Webhook
→ backend узнаёт о платеже

WebSocket
→ пользователь видит его сразу

HTTP API
→ после reconnect подтверждаем текущее состояние.

Что обычно стоит дороже

В простом проекте:

Polling
<
Webhook receiver
<
полноценная WebSocket infrastructure

по эксплуатационной сложности.

Но это не универсальная цена разработки.

Например надёжная платёжная webhook-система с:

signature;
replay;
dedupe;
reconciliation

может быть сложнее простого WebSocket notification channel.


Поэтому считать нужно конкретную функцию

Не технологию по названию.


Антипаттерн: realtime ради realtime

На dashboard есть:

число зарегистрированных пользователей.

Оно меняется несколько раз в день.

Команда строит:

WebSocket subscription.

Теперь:

connections;
Redis;
heartbeat;
reconnect;

работают ради того, чтобы число:

142

через несколько часов превратилось:

143

на 20 секунд раньше.

Ценность:

почти нулевая.

Сложность:

реальная.

Другой антипаттерн: слишком частый polling

Чат:

GET /messages
every 500 ms.

На:

20 000 users.

Это попытка построить WebSocket с помощью HTTP requests.

Здесь уже лучше real-time transport.


Третий антипаттерн: webhook напрямую выполняет всё

Webhook
↓
update database
↓
generate PDF
↓
send email
↓
sync CRM
↓
notify Telegram
↓
20 sec
↓
200

Provider получает timeout и отправляет событие повторно.

Теперь всё происходит дважды.

Лучше:

Webhook
↓
verify
↓
durable inbox
↓
200
↓
background jobs.

Четвёртый антипаттерн: WebSocket является единственным источником состояния

Пользователь перезагрузил страницу.

Все предыдущие WebSocket events исчезли.

Если без них UI не может восстановиться:

архитектура сломана.

Должен существовать обычный способ получить:

current state.

Пятый антипаттерн: webhook принимается за push-уведомление пользователю

Webhook:

server → server.

Push notification:

server → device.

Это разные технологии.


Шестой антипаттерн: WebSocket для offline mobile

Закрытое приложение не держит обычный socket.

Нужна:

push infrastructure.

Седьмой антипаттерн: polling без ограничения lifecycle

Страница job давно закрыта.

Browser tab скрыт.

Но каждые две секунды:

GET /status

продолжается часами.

Так polling становится дорогим не из-за самой технологии, а из-за плохой реализации.


Как бы выглядело техническое решение для типового клиентского сервиса

Например есть:

личный кабинет;
чат;
заказы;
оплата;
уведомления;
файлы.

WebSocket
+
durable messages in DB
+
REST resync.

Оплата

payment webhook
+
idempotent processing
+
WebSocket notification
или короткий polling экрана оплаты.

Новая заявка админу

Если требования умеренные:

polling 10 sec.

Если администратор должен реагировать мгновенно:

WebSocket.

Генерация архива

background job
+
polling 3–5 sec.

WebSocket — только если UX действительно требует непрерывного progress.


provider webhook,

если доступен.

Иначе:

scheduled polling provider API.

Уведомления

database
+
WebSocket online
+
push offline.

Получается гибридная, но логичная система.


Именно так строится зрелый real-time продукт

Не вокруг одной «мощной технологии».

А вокруг нескольких каналов с понятной ответственностью.


Простой decision tree

             Откуда приходит событие?
                /              \
          внешний сервис       наша система
               │                    │
      Provider умеет webhook?       │
          /           \             │
        да             нет          │
        │               │           │
     webhook          polling       │
     provider        provider API   │
                                      ▼
                          Нужно обновить UI?
                                /        \
                              нет         да
                                          │
                               Допустима задержка?
                                  /             \
                                да               нет
                                │                 │
                             polling       WebSocket/SSE

Это намного ближе к реальной архитектуре, чем вопрос:

WebSocket или webhook?

В реальном времени должно быть то, что действительно требует реального времени

Например:

чат

да.


онлайн presence

да.


совместное редактирование

да.


раз в сутки обновляемая статистика

нет.


статус доставки,
который сам provider обновляет раз в час

WebSocket ничего не ускорит.


Заказчику полезно знать ещё одну вещь

Каждый дополнительный realtime-механизм увеличивает не только стоимость разработки.

Но и:

стоимость тестирования;
мониторинга;
масштабирования;
поддержки;
диагностики инцидентов.

Поэтому профессиональная архитектура не стремится использовать максимальное количество технологий.

Она стремится использовать минимально необходимый набор.


Что должно попасть в ТЗ

Если заказчик хочет real-time, одной формулировки:

Всё обновляется без перезагрузки.

недостаточно.

Лучше фиксировать:

какие события;
источник каждого события;
ожидаемая задержка;
что происходит offline;
нужно ли сохранять историю;
что происходит после reconnect;
есть ли внешние providers;
какой fallback.

Вместо:

Сообщения работают в реальном времени.

Лучше:

После успешного сохранения сообщения получатель, находящийся online, должен получить уведомление о новом сообщении не позднее чем через 1 секунду при штатной работе инфраструктуры. После восстановления соединения клиент должен синхронизировать пропущенные сообщения через HTTP API.

Это уже проверяемое требование.


Вместо

Оплата отображается сразу.

Лучше:

Backend получает подтверждение оплаты через webhook платёжной системы. После фиксации успешного статуса открытый клиентский интерфейс уведомляется через realtime-канал либо обнаруживает изменение посредством временного polling. Текущее состояние оплаты всегда можно получить через API.

Теперь понятно:

кто;
что;
откуда;
как;

Так архитектура становится проверяемой

Можно написать тест:

create message
↓
store
↓
event received
< 1 sec.

Webhook:

payment event
↓
backend state = PAID
↓
duplicate event
↓
still one payment effect.

Reconnect:

disconnect
↓
three messages created
↓
reconnect
↓
all three recovered.

Без этого «real time» остаётся субъективным

Для одного заказчика:

10 секунд = сразу.

Для другого:

500 мс = медленно.

Поэтому latency должна стать частью требования.


Что выбрать первым

Для большинства обычных бизнес-приложений хорошее правило такое:

Начинайте с обычного HTTP и polling там, где задержка допустима.

Используйте webhook, когда внешний сервис умеет сам сообщать о событиях.

Добавляйте WebSocket или SSE там, где низкая задержка действительно влияет на пользовательский опыт.

Это позволяет не покупать сложность заранее.


А затем измерять

Если polling endpoint получает:

миллионы пустых requests,

можно переходить на push-модель.

Если пользователи не замечают:

5 секунд задержки,

не нужно ничего усложнять.


Архитектура должна эволюционировать вместе с нагрузкой

Например:

MVP
polling

затем:

рост
WebSocket notifications

затем:

несколько app instances
Redis Pub/Sub

затем, если действительно понадобится:

специализированный event infrastructure.

Не обязательно строить последний этап в первый день проекта.


Вместо вывода

Polling, webhook и WebSocket не являются тремя конкурирующими способами сделать одно и то же.

Они решают разные задачи.

Polling означает:

клиент сам спрашивает,
изменилось ли состояние.

Он прост и отлично работает там, где небольшая задержка допустима.

Webhook означает:

внешняя система
сама сообщает нашему backend,
что у неё произошло событие.

Он незаменим для платежей, CRM, доставки и других server-to-server интеграций.

WebSocket означает:

между клиентом и сервером
поддерживается постоянное соединение,
по которому события можно доставлять
с минимальной задержкой.

Он полезен для чатов, presence, совместной работы и действительно интерактивных интерфейсов.

При этом хороший продукт часто использует их вместе.

Например:

Платёжная система
     │
   webhook
     ▼
   Backend
     │
     ├── Database
     │
     ├── WebSocket
     │      ↓
     │   Browser
     │
     └── HTTP API
            ↑
         polling /
         reconnect sync

Поэтому требование:

Сделайте всё в реальном времени.

нужно сначала перевести с языка пожеланий на язык системы:

Что именно изменяется?
Кто создаёт событие?
Кому нужно его увидеть?
Как быстро?
Можно ли пропустить?
Что происходит offline?
Есть ли внешний provider?

И только после этого выбирать технологию.

Иногда правильный ответ будет:

WebSocket.

Иногда:

webhook.

Иногда:

обычный polling раз в 10 секунд.

А иногда — комбинация всех трёх.

Профессиональная архитектура определяется не количеством realtime-технологий.

Она определяется тем, что каждый поток данных использует самый простой механизм, который обеспечивает нужную скорость, надёжность и пользовательский опыт.

Есть похожая задача?

Опишите продукт, интеграции и ограничения. До разработки зафиксируем объём, риски и критерии приёмки.

Обсудить проект →