Real-time приложения: архитектура и технологии
Добавить YuSMP как предпочитаемый источник в Google

Коротко. Real-time приложение доставляет события пользователю за доли секунды без перезагрузки. Архитектура состоит из трёх слоёв: транспорт до клиента (WebSocket, SSE или long polling), шлюз соединений и брокер pub/sub (Redis, Kafka, NATS) между инстансами. Транспорт выбирают по направлению данных, брокер — по требуемым гарантиям доставки.
Протокол WebSocket стандартизован IETF ещё в декабре 2011 года в RFC 6455, а односторонний поток Server-Sent Events давно входит в HTML Living Standard и поддерживается всеми современными браузерами через стандартные Web API. Технологии зрелые, но архитектура real-time приложения по-прежнему остаётся местом, где проекты чаще всего спотыкаются на проде: соединения рвутся, события теряются, а сервер, который спокойно держал сотню тестовых пользователей, падает на первой рекламной кампании.
Бизнесу real-time нужен там, где задержка в несколько секунд стоит денег или доверия: чат с поддержкой, трекинг курьера или такси, биржевые котировки, совместное редактирование документов, дашборды склада и производства. Мы в YuSMP регулярно закладываем такие функции при разработке веб-приложений — от уведомлений в личном кабинете до полноценных чатов с историей и статусами прочтения.
В статье разберём, какие технологии передают данные в реальном времени и как выбрать между ними, из каких слоёв состоит бэкенд, как масштабироваться от тысячи до миллиона соединений, какие гарантии доставки реально достижимы, что меняется в мобильных приложениях и какие ошибки встречаются чаще всего. Материал рассчитан на руководителей продукта, тимлидов и аналитиков — кода здесь минимум, акцент на архитектурных решениях.
Что такое real-time приложение и чем оно отличается от обычного
Real-time приложение — это приложение, в котором сервер сам отправляет клиенту новые данные в момент их появления, а не ждёт, пока клиент о них спросит. В классической модели «запрос-ответ» браузер или мобильное приложение инициирует каждый обмен: открыл страницу — получил данные, нажал «обновить» — получил снова. В real-time модели соединение остаётся открытым, и сервер толкает (push) события по мере их возникновения.
Важно разделять понятия. В промышленной автоматике и встроенных системах «реальное время» означает жёсткие гарантии: реакция за фиксированное время, иначе — авария (hard real-time). Веб и мобильные приложения относятся к мягкому реальному времени (soft real-time): допустимая задержка — обычно от 50 до 500 мс, а редкие опоздания неприятны, но не катастрофичны. Именно о таком real-time и пойдёт речь дальше.
Почему поллинг каждые N секунд не работает
Самый простой способ «оживить» интерфейс — опрашивать сервер по таймеру (short polling). Он хорош для прототипа, но плохо масштабируется. Посчитаем: 10 000 активных клиентов, каждый опрашивает сервер раз в 2 секунды, — это 5 000 запросов в секунду. Если новые данные появляются раз в минуту, подавляющее большинство этих запросов возвращает пустой ответ, но всё равно проходит авторизацию, логирование и обращение к базе.
Вторая проблема — задержка. При опросе раз в N секунд событие в среднем доходит до пользователя через N/2 секунд, а в худшем случае через N. Уменьшая интервал, вы снижаете задержку, но линейно увеличиваете нагрузку. Real-time транспорты разрывают эту зависимость: сервер отправляет данные ровно тогда, когда они есть, и только тем, кому они нужны.
Где бизнесу нужен real-time: типовые сценарии
Real-time нужен в продуктах, где ценность данных быстро падает со временем или где несколько людей работают с одним объектом одновременно. Требования к задержке и направление потока данных у сценариев разные — и это напрямую влияет на выбор технологий.
| Сценарий | Типичное требование к задержке | Направление данных | Особенности |
| Чат, мессенджер, поддержка | до 200–500 мс | двустороннее | история, статусы «печатает» и «прочитано», presence |
| Уведомления в кабинете | 1–2 с | от сервера | важна доставка после оффлайна |
| Трекинг курьера, такси | 1–3 с | от устройства к серверу и к клиенту | частые координаты, мобильные сети |
| Котировки, торговля, аукционы | десятки–сотни мс | в основном от сервера | высокая частота, важен порядок событий |
| Дашборды, мониторинг | 1–5 с | от сервера | агрегаты, можно прореживать поток |
| Совместное редактирование | до 100–200 мс | двустороннее | разрешение конфликтов (CRDT или OT) |
| Онлайн-игры | десятки мс | двустороннее | часто нужен UDP-подобный транспорт |
| IoT-телеметрия | секунды | от устройств | огромное число источников, MQTT на краю |
Цифры в таблице — ориентиры из практики, а не стандарт: конкретный SLA задаёт бизнес. Например, для чата аналитики часто формулируют требование вида «p95 задержки доставки — не более 100 мс, p99 — не более 500 мс». Если вы планируете полноценный продукт-мессенджер, а не чат внутри сервиса, посмотрите наш разбор как создать свой мессенджер — там больше о продуктовой части.
Отдельно про совместное редактирование: когда два человека правят один документ одновременно, мало доставить изменения быстро — их нужно корректно объединить. Для этого применяют алгоритмы операционных преобразований (OT) или бесконфликтные реплицируемые типы данных (CRDT). Это отдельный слой поверх транспорта, и его сложность часто недооценивают.
Какие технологии передают данные в реальном времени
Транспорт real-time — это способ, которым события физически попадают от сервера к клиенту и обратно. В вебе выбор сводится к пяти вариантам: WebSocket, Server-Sent Events, long polling, WebTransport и push-уведомления, причём последние решают другую задачу. Разберём каждый.
WebSocket
WebSocket — это постоянный полнодуплексный канал поверх TCP: после установки соединения и клиент, и сервер могут отправлять сообщения в любой момент. Соединение начинается как обычный HTTP-запрос с заголовком Upgrade, после чего протокол «переключается» и дальше обмен идёт короткими фреймами — текстовыми или бинарными. Защищённая версия работает по схеме wss:// поверх TLS, как HTTPS.
GET /ws HTTP/1.1
Host: app.example.ru
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Плюсы WebSocket — минимальные накладные расходы на сообщение, двусторонний обмен и поддержка бинарных данных. Минусы — нужно самостоятельно реализовать переподключение, heartbeat и догрузку пропущенного, а некоторые корпоративные прокси и старые балансировщики режут долгоживущие соединения. Подробная спецификация — в RFC 6455. Как подключить WebSocket на фронтенде, написать хук и переподключение, мы разобрали отдельно в статье WebSocket в React.
Server-Sent Events (SSE)
Server-Sent Events — это односторонний поток событий от сервера к клиенту поверх обычного HTTP-ответа, который не закрывается. В браузере его читает встроенный объект EventSource. У SSE есть две полезные вещи «из коробки»: автоматическое переподключение при обрыве и заголовок Last-Event-ID, по которому сервер понимает, с какого события продолжить поток.
Ограничения SSE — только текст (бинарные данные придётся кодировать) и только направление «сервер → клиент»: отправить данные на сервер клиент может обычным HTTP-запросом. Есть и неочевидный нюанс: при HTTP/1.1 браузеры держат около шести параллельных соединений на домен, и каждая вкладка с SSE занимает одно из них. С HTTP/2 эта проблема снимается, потому что все потоки мультиплексируются в одном соединении. Формат описан в стандарте WHATWG.
Long polling
Long polling — это эмуляция push поверх обычных запросов: клиент отправляет запрос, а сервер не отвечает, пока не появится событие или не истечёт таймаут (обычно 20–30 секунд). Получив ответ, клиент сразу отправляет следующий запрос. Задержка доставки при этом почти как у WebSocket, а работает схема абсолютно везде, где работает HTTP.
Цена универсальности — заголовки и авторизация в каждом цикле, лишние соединения и нагрузка на сервер при частых событиях. Поэтому long polling обычно используют как запасной транспорт: библиотеки вроде Socket.IO и SignalR автоматически откатываются на него, если WebSocket недоступен.
WebTransport и HTTP/3
WebTransport — новый браузерный API поверх HTTP/3 и QUIC. Он даёт несколько независимых потоков в одном соединении (потеря пакета в одном потоке не тормозит остальные) и ненадёжные датаграммы — аналог UDP, полезный для игр, видео и телеметрии, где свежие данные важнее доставки каждого пакета. Поддержка в браузерах пока неполная, а серверная инфраструктура только формируется, поэтому актуальный статус стоит проверять на MDN перед стартом проекта.
Параллельно развивается и сам WebSocket: RFC 8441 описывает, как запускать WebSocket внутри потока HTTP/2 вместо отдельного TCP-соединения. Для большинства бизнес-задач в 2026 году WebSocket и SSE остаются основным выбором, а WebTransport — технологией «на вырост» для специфических сценариев.
Push-уведомления (APNs и FCM)
Push-уведомления — это не транспорт для открытого приложения, а канал, чтобы «разбудить» пользователя, когда приложение закрыто или свёрнуто. Сообщение уходит через сервис платформы — Apple Push Notification service для iOS и Firebase Cloud Messaging для Android, — а не по вашему соединению. Доставка не гарантирована по времени, размер полезной нагрузки ограничен, поэтому push используют как сигнал «есть новое», а данные приложение забирает само после открытия.
Как выбрать: WebSocket, SSE или long polling — сравнительная таблица
Выбор транспорта определяется прежде всего направлением данных, затем — сетью, в которой живут пользователи, и требованиями к масштабу. Сравнение ниже собрано по нашей практике; цифры накладных расходов — ориентиры по замерам практиков, а не стандарт, и на вашей нагрузке они будут другими.
| Критерий | WebSocket | SSE | Long polling | WebTransport |
| Направление | двустороннее | сервер → клиент | сервер → клиент (эмуляция) | двустороннее, несколько потоков |
| Протокол | TCP, Upgrade с HTTP/1.1 или поток HTTP/2 | HTTP/1.1 или HTTP/2 | HTTP | HTTP/3, QUIC |
| Задержка | минимальная | минимальная | низкая, плюс цикл переподключения | минимальная |
| Накладные расходы трафика | около 5% | около 10% | 200–400% при частых событиях | низкие |
| Прокси и корпоративные сети | иногда режутся | обычно проходят | проходят везде | UDP часто блокируется |
| Автопереподключение | писать самому | встроено | по сути встроено | писать самому |
| Бинарные данные | да | нет, только текст | да | да |
| Поддержка браузеров | все современные | все современные | любые | неполная |
| Сложность масштабирования | средняя | ниже средней | высокая нагрузка на бэкенд | высокая, мало готовых решений |
| Типичные сценарии | чаты, игры, совместная работа | уведомления, ленты, дашборды, стриминг ответов ИИ | запасной транспорт, MVP | игры, медиа, телеметрия |
Короткие правила выбора, которыми мы пользуемся на старте проекта:
- Данные идут только от сервера (уведомления, лента, статусы заказов, прогресс задачи) — берите SSE: он проще в эксплуатации и сам переподключается.
- Нужен частый обмен в обе стороны (чат с индикатором набора, совместная доска, игра) — WebSocket.
- Пользователи сидят в корпоративных сетях с жёсткими прокси — закладывайте long polling как автоматический фолбэк.
- Нужны ненадёжные датаграммы и минимальная задержка — присмотритесь к WebTransport, но с запасным вариантом на WebSocket.
- Приложение бывает закрыто — добавляйте push-уведомления поверх любого из транспортов.
На практике часто работает гибрид: SSE или WebSocket, пока пользователь в интерфейсе, push — когда он ушёл, и обычный REST для команд и загрузки истории. Не стоит заставлять один транспорт решать все задачи.
Из чего состоит архитектура real-time приложения
Архитектура real-time приложения — это цепочка компонентов, которая принимает событие в бизнес-логике и доставляет его всем нужным клиентам с заданной задержкой. Независимо от транспорта, путь события в зрелой системе выглядит примерно так:
- Клиент — браузер или мобильное приложение держит открытое соединение и подписывается на нужные каналы (комната чата, заказ, дашборд).
- Балансировщик или API Gateway — терминирует TLS, пробрасывает заголовки Upgrade, держит длинные таймауты и распределяет соединения по инстансам шлюза.
- Шлюз соединений — отдельный сервис, который хранит только открытые соединения и подписки и умеет быстро отправить сообщение нужным клиентам.
- Брокер pub/sub — шина, через которую бизнес-сервисы публикуют события, а инстансы шлюза получают те, на которые подписаны их клиенты.
- Бизнес-сервисы — обрабатывают команды, проверяют права, меняют состояние и публикуют события.
- Хранилища — основная база данных для истории и кэш (часто Redis) для быстрого состояния: кто онлайн (presence), последние события, курсоры клиентов.

Такая схема хорошо ложится на сервисную архитектуру, но не требует её: на старте шлюз и бизнес-логика могут жить в одном монолите, если между ними уже есть граница через брокер. Если вы только выбираете между монолитом и сервисами, начните с нашего объяснения, что такое микросервисы простыми словами.
Шлюз соединений отдельно от бизнес-логики
Главное архитектурное решение — вынести долгоживущие соединения в отдельный слой. Причин три. Первая — деплой: бизнес-логику вы выкатываете несколько раз в день, и если соединения живут в том же процессе, каждый релиз рвёт их у всех пользователей. Вторая — профиль нагрузки: шлюзу нужна память на соединения и сеть, бизнес-сервисам — процессор и доступ к базе, масштабировать их выгоднее независимо. Третья — изоляция сбоев: зависший запрос к базе не должен блокировать отправку сообщений сотне тысяч клиентов.
Шлюз при этом остаётся «тупым»: он проверяет токен при подключении, хранит подписки и пересылает сообщения, но не принимает бизнес-решений. Готовые серверы вроде Centrifugo построены именно по этому принципу.
Модель событий и контракт
Контракт событий — это договорённость о формате сообщений между бэкендом, вебом и мобильными клиентами. Без него real-time быстро превращается в россыпь несовместимых JSON, которые ломаются при каждом релизе. Минимальный конверт события выглядит так:
{
"type": "order.status_changed",
"id": "01JB7Q2W8K3M5N",
"ts": "2026-10-20T09:15:02.341Z",
"version": 2,
"channel": "order:48213",
"data": { "status": "courier_assigned", "eta_min": 14 }
}
Поле id нужно для идемпотентности и догрузки, ts — для порядка и метрик задержки, version — для эволюции схемы без поломки старых мобильных клиентов, которые пользователи не обновляют месяцами. Каналы, типы событий и схемы удобно описывать в спецификации AsyncAPI — это аналог OpenAPI для событийных интерфейсов.
Pub/Sub и брокеры сообщений: Redis, Kafka, RabbitMQ, NATS
Pub/Sub (издатель-подписчик) — это шаблон обмена, в котором отправитель публикует событие в канал или топик, не зная, кто его получит, а брокер доставляет копию каждому подписчику. Шаблон подробно описан в центре архитектуры Microsoft Azure. В real-time системе брокер решает ключевую задачу: клиент подключён к одному инстансу шлюза, а событие возникло в сервисе, который ничего не знает об этом инстансе.
| Брокер | Модель | Гарантии доставки | Хранение и повтор | Когда брать |
| Redis Pub/Sub | каналы, рассылка в памяти | at-most-once | нет: оффлайн-подписчик сообщение теряет | fan-out между инстансами шлюза, presence, «печатает…» |
| Redis Streams | журнал с группами потребителей | at-least-once с подтверждениями | да, ограниченно по объёму памяти | догрузка после реконнекта, небольшие очереди событий |
| Apache Kafka | распределённый журнал, партиции | at-least-once, есть транзакции | да, долговременно, перечитывание по offset | большой поток событий, аналитика, event sourcing |
| RabbitMQ | очереди и обменники с маршрутизацией | at-least-once с подтверждениями | пока сообщение не обработано | задачи и команды со сложной маршрутизацией |
| NATS / JetStream | лёгкие subject-ы, JetStream — с хранением | Core — at-most-once, JetStream — at-least-once | в JetStream — да | низкая задержка, много каналов, простая эксплуатация |
Чаще всего путаница возникает вокруг Redis. Классический Redis Pub/Sub работает по модели at-most-once: если подписчик в момент публикации не подключён, сообщение для него просто исчезает. Для мгновенного fan-out между инстансами шлюза это нормально, а для событий, которые нельзя потерять, — нет. Там нужны Redis Streams, JetStream или Kafka.
Apache Kafka хранит события в журнале, и любой потребитель может перечитать их с нужного места. Это мощно, но Kafka не предназначена для миллионов мелких динамических каналов вроде «комната чата №48213» и требует серьёзной эксплуатации. Типичная зрелая схема — Kafka как шина бизнес-событий между сервисами и Redis или NATS для быстрой раздачи клиентам через шлюз.
Как масштабировать real-time: от 1 000 до 1 000 000 соединений
Масштабирование real-time — это рост числа одновременных соединений и событий без роста задержки. До нескольких тысяч соединений справляется один сервер почти без усилий. Дальше начинаются архитектурные решения:
- Горизонтальное масштабирование шлюза. Несколько одинаковых инстансов за балансировщиком; каждый держит свою часть соединений и подписывается в брокере только на каналы своих клиентов.
- Общий брокер вместо sticky sessions. Привязка клиента к конкретному серверу (sticky sessions) нужна для long polling и некоторых библиотек, но не решает доставку между инстансами. Событие должно уходить через брокер на все инстансы, где есть подписчики канала.
- Шардирование каналов. При сотнях тысяч комнат одна шина становится узким местом — каналы распределяют по шардам брокера по хешу идентификатора.
- Лимиты операционной системы. Каждое соединение — это файловый дескриптор и память под буферы. Лимиты дескрипторов, диапазон портов балансировщика и параметры TCP-стека настраивают заранее, а не после первого падения.
- Backpressure и медленные клиенты. Клиент на плохом мобильном интернете не успевает принимать сообщения, и буфер на сервере растёт. Нужен лимит исходящего буфера на соединение: при переполнении — прореживать некритичные события или отключать клиента с просьбой переподключиться.
- Graceful-деплой. При выкатке новой версии шлюза инстансы выводят из балансировки постепенно и закрывают соединения порциями, а не все сразу.
- Защита от шторма переподключений. Если сто тысяч клиентов отвалились одновременно, они не должны так же одновременно вернуться. Переподключение — с экспоненциальной задержкой и случайным джиттером, а на сервере — лимит скорости новых подключений.
Отдельная статья расходов — fan-out. Сообщение в канал с десятью подписчиками дёшево, а одно событие для ста тысяч подписчиков (например, изменение котировки) означает сто тысяч отправок. Такие «широкие» каналы проектируют отдельно: прореживают частоту, агрегируют обновления и раздают их пачками.
Надёжность: гарантии доставки, порядок и переподключение
Надёжность real-time системы определяется тем, что происходит с событием при сбоях: обрыве сети, перезапуске сервера, уходе клиента в оффлайн. Есть три уровня гарантий доставки:
- At-most-once — событие доставляется не более одного раза и может потеряться. Подходит для индикатора «печатает…», позиции курсора, промежуточных котировок.
- At-least-once — событие доставляется точно, но иногда дважды. Это основной рабочий режим для сообщений чата, статусов заказов, уведомлений.
- Effectively-once — at-least-once плюс идемпотентная обработка на клиенте. Настоящая «ровно один раз» доставка по сети в общем случае недостижима, поэтому её эмулируют дедупликацией по
idсобытия.
Порядок. Глобальный порядок всех событий системы дорог и почти никогда не нужен. Достаточно гарантировать порядок в рамках канала: все события одного чата или одного заказа идут через одну партицию или шард и нумеруются последовательно. Клиент, получив событие с номером 15 после номера 13, понимает, что пропустил 14, и запрашивает его.
Догрузка после реконнекта. При переподключении клиент передаёт последний полученный идентификатор: в SSE это делает сам браузер через Last-Event-ID, в WebSocket — ваш протокол, в Kafka — offset потребителя. Сервер досылает пропущенное из журнала или из базы. Без этого механизма каждый обрыв сети — это потерянные сообщения, и пользователи обнаруживают их только при перезагрузке страницы.
Heartbeat. TCP-соединение может «умереть» молча — например, при смене сети у мобильного клиента. Поэтому стороны регулярно обмениваются ping-pong сообщениями (в WebSocket для этого есть служебные фреймы). Нет ответа за несколько интервалов — соединение считается мёртвым, сервер освобождает ресурсы, клиент переподключается.
Оффлайн-очередь на клиенте. Сообщения, которые пользователь отправил без сети, складываются в локальную очередь и уходят после восстановления связи — с теми же идентификаторами, чтобы сервер не создал дубли.
Real-time в мобильных приложениях: что учесть
Real-time в мобильном приложении отличается от веба тем, что операционная система, а не разработчик, решает, когда приложению можно держать соединение. iOS и Android агрессивно ограничивают фоновую работу ради батареи: через короткое время после сворачивания сокеты закрываются, а фоновые задачи откладываются.
Поэтому рабочая схема почти всегда гибридная: WebSocket или SSE, пока приложение на экране, и push-уведомления через APNs и FCM, когда оно в фоне или закрыто. При возврате в приложение клиент переподключается и синхронизирует состояние — догружает пропущенные события по курсору, а не перезапрашивает всё целиком.
Что ещё важно закладывать в архитектуру мобильного клиента:
- Смена сети. Переход с Wi-Fi на LTE меняет IP-адрес и рвёт TCP-соединение. Клиент должен быстро заметить это и переподключиться, не дожидаясь таймаута heartbeat.
- Батарея. Слишком частый heartbeat держит радиомодуль активным и заметно расходует заряд. Интервал подбирают как компромисс между скоростью обнаружения обрыва и энергопотреблением.
- Нестабильный канал. В метро и лифте связь пропадает на секунды — интерфейс должен показывать статус «соединение…», а не ошибку, и хранить неотправленное в оффлайн-очереди.
- Старые версии. Мобильные клиенты обновляются медленно, поэтому версионирование контракта событий здесь критично.
Если real-time функции нужны и в вебе, и в приложениях под iOS и Android, протокол событий и логику синхронизации лучше проектировать сразу для всех платформ. Мы делаем это в рамках проектов по разработке мобильных приложений — один контракт событий для веба и мобильных клиентов экономит месяцы на поддержке.
Безопасность real-time соединений
Безопасность real-time соединений строится на тех же принципах, что и для REST, но с поправкой на то, что соединение живёт долго, а проверка «на входе» происходит один раз. Базовый набор мер:
- Только шифрованные соединения.
wss://и HTTPS для SSE — без исключений, включая внутренние стенды, доступные снаружи. - Аутентификация при подключении. Короткоживущий токен проверяется при handshake. Браузерный WebSocket не позволяет передать произвольные заголовки, поэтому токен часто кладут в query-строку — тогда это должен быть одноразовый тикет на несколько секунд, иначе он осядет в логах прокси.
- Проверка Origin. Сервер отклоняет подключения с чужих доменов — это защита от межсайтового перехвата WebSocket.
- Авторизация на каждый канал. Подключиться — не значит получить доступ ко всем комнатам. Подписка на канал проверяется отдельно, а при отзыве прав соединение должно отписываться или разрываться.
- Истечение токена. Соединение может жить дольше токена; нужен механизм обновления токена без переподключения или принудительный разрыв по истечении.
- Лимиты. Ограничения на число соединений с одного пользователя и IP, на частоту и размер сообщений, на число подписок. Без них один клиент может занять ресурсы, рассчитанные на тысячи.
- Валидация входящих сообщений. Всё, что приходит от клиента по сокету, проверяется так же строго, как тело HTTP-запроса.
Готовые платформы или своя инфраструктура
Готовая real-time платформа — это облачный сервис, который берёт на себя соединения, масштабирование и доставку, а вы публикуете события через API. К этому классу относятся Firebase, Supabase Realtime, Azure Web PubSub, Ably и Pusher. Альтернатива — своя инфраструктура на открытых решениях: Socket.IO для Node.js, SignalR для .NET, Centrifugo как отдельный сервер плюс брокер.
| Критерий | Managed-платформа | Своя инфраструктура |
| Скорость старта | дни: SDK и готовая консоль | недели: шлюз, брокер, мониторинг |
| Стоимость на малом объёме | низкая, часто бесплатный тариф | фиксированные затраты на серверы и DevOps |
| Стоимость на большом объёме | растёт с соединениями и сообщениями | растёт медленнее, предсказуема |
| Контроль данных и 152-ФЗ | данные у провайдера, часто за рубежом | полный контроль, хранение в РФ |
| Доступность из России | часть сервисов ограничена или рискованна | зависит только от вас |
| Vendor lock-in | высокий: свой SDK и модель данных | низкий, стандартные протоколы |
| Гибкость логики | в рамках возможностей платформы | любая |
Для российского бизнеса отдельный аргумент — закон 152-ФЗ: персональные данные граждан РФ должны записываться и храниться в базах на территории России, а переписка в чате почти всегда содержит персональные данные. Плюс ряд зарубежных сервисов ограничил работу с российскими компаниями. Поэтому managed-платформы мы чаще рекомендуем для прототипов и внутренних инструментов без персональных данных, а продакшн — на собственной инфраструктуре или у российского облачного провайдера.
Как выглядит real-time на конкретном BaaS-решении, включая структуру данных и правила безопасности, разобрано в статье Firebase Realtime Database: чат, код, архитектура.
Как спроектировать real-time систему: 7 шагов
Проектирование real-time системы — это последовательность решений от бизнес-требований к инфраструктуре. Мы проходим эти шаги на этапе аналитики, до написания кода:
- Зафиксируйте сценарии и SLA. Какие события, кому и с какой задержкой доставлять (p95 и p99), сколько пользователей онлайн в пике и сколько событий в секунду. Без этих цифр невозможно ни выбрать технологии, ни проверить результат.
- Выберите транспорт по направлению данных. SSE — для потока от сервера, WebSocket — для двустороннего обмена, long polling — как фолбэк, push — для фона и оффлайна.
- Опишите контракт событий. Конверт с
type,id,tsиversion, список каналов и правила доступа к ним. Удобный формат — AsyncAPI. - Выберите брокер по гарантиям доставки. Можно ли потерять событие? Нужно ли перечитывать историю? От ответов зависит выбор между Redis Pub/Sub, Redis Streams, NATS JetStream и Kafka.
- Спроектируйте шлюз и масштабирование. Отдельный слой соединений, балансировка, fan-out через брокер, лимиты ОС и буферов, порядок деплоя без массовых обрывов.
- Заложите надёжность и мониторинг. Heartbeat, переподключение с экспоненциальной задержкой и джиттером, догрузка по курсору, оффлайн-очередь, метрики и алерты.
- Проведите нагрузочный тест соединений. Целевое число одновременных подключений, массовое переподключение, медленные клиенты, отказ одного инстанса — всё это проверяется до релиза.
Мониторинг real-time: какие метрики смотреть
Мониторинг real-time системы отличается от мониторинга REST API тем, что главная единица — не запрос, а соединение и событие. Стандартные графики RPS и времени ответа здесь почти ничего не показывают. Минимальный набор метрик:
- Активные соединения — всего и по инстансам; резкое падение означает массовый обрыв, перекос — проблему балансировки.
- Скорость подключений и отключений — всплеск переподключений говорит о сетевой проблеме или сбое шлюза раньше, чем пожалуются пользователи.
- End-to-end задержка доставки — от публикации события до получения клиентом, в перцентилях p50, p95, p99. Именно её сравнивают с SLA.
- Лаг брокера (consumer lag) — насколько потребители отстают от публикации; растущий лаг — признак того, что система не успевает.
- Размер исходящих буферов — растущие буферы означают медленных клиентов или перегрузку сети.
- Ошибки аутентификации и отклонённые подписки — индикатор и багов в клиенте, и попыток злоупотребления.
Типовой стек — Prometheus для сбора метрик и Grafana для дашбордов и алертов. Обязательные алерты: падение числа соединений больше чем на заданный процент за минуту, рост p99 задержки выше SLA, шторм переподключений и рост consumer lag.
Типичные ошибки при разработке real-time функций
Большинство проблем real-time на проде повторяются от проекта к проекту. Вот ошибки, которые мы чаще всего видим на аудитах:
- WebSocket там, где хватит SSE. Двусторонний канал для уведомлений «сервер → клиент» добавляет работы по переподключению и проксированию без какой-либо пользы.
- Бизнес-логика в шлюзе. Каждый релиз рвёт все соединения, а тяжёлый запрос к базе тормозит доставку всем клиентам инстанса.
- Нет догрузки после обрыва. Сообщения, пришедшие за время переподключения, теряются молча.
- Redis Pub/Sub для событий, которые нельзя терять. Модель at-most-once не подходит для платежей, заказов и сообщений чата без отдельного хранения.
- Нет лимитов. Ни на число соединений, ни на размер и частоту сообщений — до первого клиента с ошибкой в цикле отправки.
- Забытые таймауты прокси. В nginx параметр
proxy_read_timeoutпо умолчанию равен 60 секундам: соединение без трафика дольше минуты будет разорвано. Нужны heartbeat чаще этого интервала или увеличенный таймаут. - Тестирование на десяти пользователях. Проблемы real-time проявляются на тысячах соединений и при массовом переподключении — без нагрузочного теста вы узнаете о них от пользователей.
Пример минимальной настройки nginx для проксирования WebSocket:
location /ws/ {
proxy_pass http://ws_gateway;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 120s;
}
Заключение
Хорошая архитектура real-time приложения строится не вокруг модного протокола, а вокруг требований: какие события, кому, с какой задержкой и что делать, если событие не дошло. Транспорт выбирают по направлению данных, брокер — по гарантиям доставки, а надёжность обеспечивают переподключение, догрузка по курсору и мониторинг задержки. Если заложить это на этапе проектирования, real-time функции масштабируются вместе с продуктом, а не становятся источником ночных инцидентов.
Найдем лучшее решение для вас
Частые вопросы (FAQ)
Что такое real-time приложение простыми словами?
Real-time приложение — это приложение, в котором сервер сам присылает пользователю новые данные сразу, как только они появились, без перезагрузки страницы и без постоянных запросов «есть ли что-то новое». Так работают мессенджеры, трекинг курьера на карте, биржевые котировки и онлайн-доски для совместной работы. Обычно задержка доставки составляет доли секунды.
Чем WebSocket отличается от SSE?
WebSocket — двусторонний канал: и клиент, и сервер могут отправлять сообщения в любой момент, поддерживаются текст и бинарные данные. SSE (Server-Sent Events) — односторонний поток от сервера к клиенту поверх обычного HTTP, только текст, зато со встроенным автопереподключением и догрузкой по Last-Event-ID. Если данные идут только от сервера, SSE проще и дешевле в поддержке.
Когда достаточно long polling?
Long polling подходит, когда событий немного, пользователей сотни или единицы тысяч, а приложение должно работать в любых сетях, включая корпоративные прокси, которые режут WebSocket. Это хороший запасной транспорт и быстрый способ запустить MVP. При частых событиях и большом числе клиентов long polling становится дорогим по трафику и нагрузке на сервер.
Сколько соединений выдерживает один сервер WebSocket?
Точной цифры нет: всё зависит от памяти на соединение, частоты сообщений, логики обработки и настроек операционной системы. При лёгких соединениях без тяжёлой логики один инстанс шлюза держит десятки, а иногда и сотни тысяч подключений. Это ориентир, а не гарантия: реальный потолок для вашей системы показывает только нагрузочный тест.
Нужен ли Kafka для чата?
Для MVP и большинства чатов Kafka не нужна: доставку между инстансами шлюза закрывают Redis или NATS, а историю сообщений хранят в базе данных. Kafka оправдана, когда нужен долговременный журнал событий, перечитывание с любого места, аналитика в потоке и очень большой объём сообщений. Начинать с Kafka без такой потребности — лишняя сложность в эксплуатации.
Как real-time работает в мобильном приложении в фоне?
iOS и Android закрывают сетевые соединения приложений, ушедших в фон, чтобы беречь батарею. Поэтому WebSocket держат только пока приложение открыто, а в фоне пользователя будят push-уведомлениями через APNs и FCM. После возврата в приложение клиент переподключается и догружает пропущенные события по последнему известному идентификатору или курсору.
Сколько стоит добавить real-time в существующее приложение?
Стоимость зависит от сценариев, числа пользователей, требований к гарантиям доставки и того, насколько готов текущий бэкенд к событийной модели. Добавить уведомления через SSE — одна задача, а построить чат с историей, статусами прочтения и мобильными push — совсем другая. Корректная оценка возможна после технического аудита и описания сценариев.
Нужны real-time функции в вашем продукте?
Спроектируем и разработаем веб-приложение с чатами, уведомлениями и live-обновлениями — от выбора протокола до нагрузочного теста.


