Главная / Статьи

Выбор транспорта и архитектуры для приложений на JavaScript в реальном времени

Как WebSockets, Socket.IO, WebRTC, брокеры сообщений, регионы края сети и системы мониторинга взаимодействуют при создании чатов, прямой трансляции или многопользовательских игр на JavaScript.

Издатель: REACTAPP.TOP8 мин чтения1904 слов

Источники: Real-Time Applications with JavaScript (Chat, Streaming, Gaming)

Пользователи больше не терпят необходимости обновления страницы, чтобы узнать, изменилось ли что-то. Ожидается, что сообщение в чате, курьер, доставляющий товар, счёт в матче или изменение цены появятся мгновенно после события, и любая заметная задержка воспринимается как сбой продукта. В этом руководстве рассматриваются инструменты, предоставляемые JavaScript для создания такого опыта, начиная с уровня передачи данных и заканчивая масштабированием и возможностями отслеживания, чтобы вы могли выбрать подходящие элементы для чат-системы, функции стриминга или браузерной игры и понимать, что может стать проблемой при росте нагрузки.

Ранний веб функционировал как газета, печатаемая по запросу. Вы открывали страницу, читали её, нажимали на ссылку и ждали загрузки следующего документа. Ничего нового к вам не попадало, если только вы сами этого не запрашивали, и каждый запрос означал полный цикл передачи данных.

Современные продукты меняют эту ситуацию. Сообщения отправляются мгновенно после нажатия кнопки «Отправить», результаты спортивных матчей обновляются во время игры, экраны торговли постоянно обновляются, многопользовательские сессии позволяют всем участникам действовать синхронно, а прямая трансляция видео достигает широкой аудитории с минимальной задержкой. Общим для всех этих случаев является то, что инициатива исходит от сервера: вместо того чтобы клиент постоянно запрашивал «есть ли что-то новое?», сервер передает данные каждому заинтересованному устройству по мере возникновения событий.

Это и есть практическое определение приложения в реальном времени: разница между моментом возникновения события и моментом, когда пользователь видит его результат, минимизируется. Типичные примеры включают:

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

    JavaScript работает в браузере, а также, благодаря Node.js, на сервере, поэтому команда может писать код для обоих концов живого соединения на одном языке и обмениваться между собой типами данных, правилами валидации и форматами сообщений.

    Более важной причиной является модель выполнения. Node.js работает по принципу обработки событий и использует неблокирующий ввод-вывод: один цикл событий ожидает, пока сокеты станут читаемыми или записываемыми, и запускает небольшую функцию-ответ при этом, вместо того чтобы выделять отдельную нить для каждого соединения. Серверы реального времени тратят большую часть времени на поддержание тысяч в основном неактивных соединений, что именно тот тип задач, который этая модель обрабатывает с минимальными затратами. Стоит помнить и об обратной стороне: один медленный синхронный расчёт блокирует все соединения в данном процессе, поэтому задачи, требующие больших вычислительных ресурсов CPU, следует выполнять в рабочих нитях или отдельных сервисах. Чтобы узнать подробнее о том, как цикл событий и пул нитей делят эту работу, ознакомьтесь с тем, как взаимодействуют libuv, цикл событий и пул нитей.

    Чаты и обмен сообщениями через WebSockets

    Обычный HTTP представляет собой взаимодействие в формате запрос-ответ: браузер задает вопрос, сервер отвечает, и обмен завершается. Для чата это не подходит, поскольку сервер может отправлять сообщения клиенту в непредсказуемые моменты, а клиент не может знать, когда следует задавать запросы. Использование таймеров для опроса либо приводит к избыточному количеству запросов, либо увеличивает задержку.

    WebSockets решают эту проблему, преобразуя HTTP-соединение в постоянный полудуплексный канал. После установления соединения любая из сторон может отправлять данные в любое время, что обеспечивает:

    • мгновенную передачу данных в обоих направлениях
    • отсутствие дополнительных запросов или заголовков для каждого сообщения
    • низкую задержку, поскольку соединение уже открыто

    Браузеры поставляются с встроенным API WebSocket, а Node.js имеет надежные серверные библиотеки. Многие команды предпочитают Socket.IO вместо простых сокетов благодаря дополнительным практическим функциям: автоматической переподключаемости, альтернативным способам передачи данных при невозможности установления WebSocket-соединения, возможности группировки пользователей в комнаты, передачи сообщений множеству клиентов, а также API с именованными событиями вместо ручной обработки сообщений. Следует отметить, что Socket.IO использует собственный протокол, поэтому клиент Socket.IO должен взаимодействовать только с сервером Socket.IO. Если вам нужен подробный обзор функций комнат, сохранения данных и масштабирования для чат-бэкенда, наш гид по чат-бэкендам в реальном времени рассматривает эти аспекты.

    Аудио- и видеотрансляция в реальном времени, а также обмен экраном с помощью WebRTC

    Стриминг является одной из наиболее объемных категорий интернет-трафика и включает в себя стримы игр, онлайн-курсы, видеозвонки, спортивные трансляции и презентации продуктов. Для интерактивных медиа в браузере ключевой технологией является WebRTC, который поддерживает:

    • стримы видео и аудио
    • обмен экраном
    • прямые соединения типа «человек-к-человеку» между браузерами

    Поскольку медиа может передаваться напрямую между устройствами без использования ваших серверов, WebRTC обеспечивает низкую задержку и экономит пропускную способность серверов, сохраняя при этом высокое качество звука и видео, поэтому он лежит в основе многих инструментов для проведения конференций. На практике все же требуется канал сигнализации (часто WebSocket), чтобы устройства могли найти друг друга, а также ретрансляционные серверы для сетей, где прямое соединение невозможно. Для трансляций от одного исполнителя к большой аудитории чистый формат «человек-к-человеку» перестает быть эффективным, и тогда берут на себя функции передачи медиа серверы.

    Многопользовательские игры в браузере

    Игры — это самый капризный случай: каждое движение должно почти мгновенно достигать других игроков, иначе процесс игры кажется некорректным. Многопользовательская игра в браузере обычно использует WebSockets для сетевого соединения, Node.js на сервере, Canvas или WebGPU для отрисовки, а также движок физики для обработки движения и столкновений.

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

    Проектирование с учётом событий

    Каждая система в реальном времени генерирует непрерывный поток событий: новые сообщения, подключение пользователей, подтверждения оплаты, действия в игре, уведомления и обновления потока. JavaScript естественным образом подходит для кода, основанного на событиях, поэтому вместо постоянной проверки на наличие событий обработчики запускаются только тогда, когда оно действительно происходит.

    Такая архитектура способствует улучшению производительности, масштабируемости и эффективности использования ресурсов. Node.js может одновременно обрабатывать большое количество параллельных операций в рамках своего цикла событий, не выделяя отдельную нить для каждого соединения, что позволяет сохранять низкий уровень использования памяти на клиенте.

    Масштабирование за пределы одного сервера с помощью посредников сообщений

    Рано или поздно один процесс не сможет обрабатывать все соединения. Представьте платформу обмена сообщениями с миллионами пользователей: два участника одного разговора могут быть подключены к разным серверам, а сообщение может пройти через несколько сервисов на стороне сервера, прежде чем дойти до получателя.

    Посредники сообщений, такие как Apache Kafka и RabbitMQ, координируют этот трафик, надежно распределяя события между серверами приложений. Они обеспечивают:

    • горизонтальную масштабируемость, поскольку добавляются новые серверы вместо того, чтобы развивать один
    • устойчивость к сбоям при отказе какого-либо инстанса
    • надежную доставку событий
    • высокую пропускную способность при нагрузке

    Redis также часто используется в качестве легковесного слоя pub/sub, например для распространения сообщений Socket.IO среди узлов. Большинство продуктов в реальном времени практически всегда опираются на какую-либо инфраструктуру обмена сообщениями.

    Сокращение задержек с помощью развертывания на периферии и в нескольких регионах

    Расстояние — это задержка. Если каждый пользователь взаимодействует с одним удаленным центром обработки данных, каждая поездка туда и обратно влечет за собой эту задержку, независимо от скорости вашего кода. Распределенные вычисления приближают обработку к пользователям, что обеспечивает более быстрые ответы, меньшую сетевую задержку, лучшее качество стриминга и более быструю реакцию в играх.

    Именно по этой причине сервисы на JavaScript все чаще развертываются в нескольких географических регионах. Для аудитории во всем мире это может значительно улучшить воспринимаемую скорость, но создает новую проблему: состояние, хранящееся в нескольких регионах, должно оставаться согласованным, поэтому заранее необходимо определить, какие данные требуют единого хранилища.

    Наблюдаемость для реальных систем

    В продуктах в режиме реального времени задержка в несколько сотен миллисекунд уже заметна, поэтому требуется непрерывный мониторинг, а не периодические проверки. К полезным метрикам относятся:

    • количество открытых соединений и активных пользователей
  • Пропускная способность событий
  • Задержка сообщений и время доставки
  • Уровень ошибок
  • Использование памяти и процессора
  • Количество пакетов в секунду и пропускная способность сети
  • Уровень успешности повторной подключения
  • Популярным сочетанием для сбора метрик является Prometheus и Grafana для создания панелей управления. Недостаточно просто отслеживать средние значения; необходимо также следить за процентилями задержек, поскольку именно небольшая доля медленных доставок становится причиной жалоб пользователей. Эффективный мониторинг позволяет выявить узкие места до того, как они приведут к сбоям.

    Где встречаются эти паттерны

    Те же основные компоненты используются в совершенно разных продуктах:

    • Чаты: сообщения передаются мгновенно между устройствами.
    • Видеоконференции: участники обмениваются аудио и видео в реальном времени.
    • Онлайн-игры: игроки соревнуются в одном синхронизированном мире.
    • Финансовые платформы: цены обновляются по мере совершения сделок.
    • Сервисы совместных поездок: водители и пассажиры постоянно видят местоположение друг друга.
    • Доставка еды: клиенты могут отслеживать ход выполнения заказа в реальном времени.
    • Совместная работа над документами: несколько человек одновременно редактируют один и тот же документ.

    Сложные аспекты

    Поддержание активных соединений и свежести данных создаёт проблемы, с которыми редко сталкиваются приложения с моделью запрос-ответ:

    • задержки в сети, различающиеся в зависимости от пользователя и региона
    • потеря соединений, особенно в мобильных сетях
    • порядок доставки сообщений при нерегулярном поступлении событий
    • масштабирование долговременных соединений
    • синхронизация состояния между клиентами и серверами
    • рост объёма памяти из-за большого количества открытых сокетов
    • уязвимости безопасности в непроверенных или неавторизованных каналах связи

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

    Чек-лист для производственной среды

    • Для частых обновлений предпочтительнее использовать WebSockets вместо повторного запрашивания данных.
    • Сохраняйте небольшие размеры сообщений и компрессируйте данные там, где это возможно.
    • Аутентифицируйте каждое соединение, а не только при первой загрузке страницы.
    • Внедряйте ограничения на частоту запросов для каждого соединения или пользователя.
    • Расширяйте масштабы горизонтально и передавайте события через брокер или слой pub/sub.
    • Храните в кэше данные, которые часто запрашивают многие клиенты.
    • Непрерывно отслеживайте работоспособность системы.

    Распространённая архитектура, соответствующая этому списку, объединяет Node.js, Socket.IO и WebRTC с Redis и Apache Kafka; она развертывается с использованием инструментов оркестрации контейнеров за облаковыми балансировщиками нагрузки и отслеживается с помощью панелей мониторинга. Такая тщательно спроектированная архитектура может обслуживать очень большое количество одновременных пользователей при сохранении низкой задержки.

    Куда движется реальное время в JavaScript

    Несколько тенденций расширяют возможности приложений в реальном времени: функции совместной работы, основанные на ИИ, многопользовательские игры с использованием WebGPU, приложения, разработанные нативно для периферийных устройств, облачные игры в браузере, погружающаяся виртуальная реальность, помощники на основе ИИ в реальном времени и видео с ещё более низкой задержкой. По мере улучшения инфраструктуры мгновенный ответ становится стандартным ожиданием, а не отличительной особенностью.

    Это также делает соответствующие навыки ценными: программирование на основе событий, распределённые системы и сетевая коммуникация, а также масштабируемый дизайн бэкенда, архитектура с низкой задержкой и разработка в стиле cloud-native востребованы в сферах финтеха, здравоохранения, игр и социальных платформ.

    Основные выводы

    • Выбирайте способ передачи данных в зависимости от характера трафика: WebSockets или Socket.IO для двунаправленной передачи сообщений, WebRTC — для медиафайлов.
    • Цикл событий делает Node.js эффективным для поддержки большого количества соединений, при условии отсутствия блокирующих операций внутри него.
    • Заранее планируйте использование нескольких серверов; брокеры или механизмы типа pub/sub определяют, как события передаются между инстансами.
    • Задержка также зависит от географии, поэтому при глобальной аудитории размещайте серверы рядом с пользователями.
    • Проектируйте систему с учётом возможных сбоев: восстановление соединения, обеспечение порядка обработки операций и аутентификация — это необходимые требования, а не дополнительные улучшения.
  • Измеряйте процентили задержек и скорость восстановления соединения, а не только время работы.