SSE vs WebSockets: разобрались, что где лучше работает

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

221 открытий2К показов
SSE vs WebSockets: разобрались, что где лучше работает

Сегодня в веб-разработке есть много технологий для обмена данными между сервером и клиентом в реальном времени. Среди них особое место занимают SSE (Server-Sent Events) и WebSockets. Обе технологии позволяют создавать динамичные приложения, такие как чаты, онлайн-игры или панели мониторинга. Но они работают по-разному и подходят для разных задач.

Вместе с Марком Садыковым, главным инженером по разработке в Сбере и ментором Эйч Навыки, разбираемся как работают SSE и WebSockets, чем они отличаются и даже как ответить на вопросы о них на собеседовании.

Что такое WebSockets и SSE и как они работают на базовом уровне?

Существуют 4 главные стратегии общения клиента и сервера в реальном времени: WebSocket, Server-Sent Events, Long Polling и Short Polling. Нужно четко понимать различие в стратегиях, чтобы построить быстрое и поддерживаемое приложение, хотя и принято в любой непонятной ситуации использовать WebSocket.

WebSocket — протокол связи поверх TCP-соединения для обмена сообщениями между клиентом и сервером, он использует постоянное соединение.

SSE (Server-Sent Events) — push-технология (связь инициируется сервером, а не клиентом), позволяющая клиенту получать автоматические обновления с сервера через HTTP-соединения.

Эти технологии позволяют поддерживать постоянное соединение между клиентом и сервером для обмена данными в реальном времени. Информация передается и обновляется мгновенно или почти мгновенно с учетом физических задержек сети. Это особенно полезно в приложениях, где важна минимальная задержка в передаче данных: чаты, онлайн-игры, уведомления. Безусловно реальное время можно реализовать посредством Long Polling и Short Polling, но это потребует больше ресурсов разработки, дальнейшей поддержки, плюс это довольно сложно сделать эффективно.

SSE vs WebSockets: разобрались, что где лучше работает 1

Вот как это выглядит в виде схемы:

WebSocket

SSE vs WebSockets: разобрались, что где лучше работает 2

SSE

SSE vs WebSockets: разобрались, что где лучше работает 3

Long Pooling

SSE vs WebSockets: разобрались, что где лучше работает 4

Short Pooling

SSE vs WebSockets: разобрались, что где лучше работает 5

В общем случае для реального времени в вебе лучше использовать WebSocket и SSE.

WebSocket можно сравнить с телефонным разговором. Когда вы звоните другу, вы устанавливаете связь (как начальный Handshake в WebSocket). После этого линия остается постоянно открытой, и вы можете говорить друг с другом долго, не прерывая разговор и не набирая номер заново для каждого слова. Вы оба можете говорить и слушать одновременно (двусторонняя связь), и все, что вы говорите, доходит до собеседника мгновенно. Если вдруг сигнал теряется, вы можете перезвонить.

SSE vs WebSockets: разобрались, что где лучше работает 6

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

SSE vs WebSockets: разобрались, что где лучше работает 7

Принцип работы WebSocket

Установка соединения (WebSocket Handshake)

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

  • Клиент отправляет HTTP-запрос с методом GET на сервер и заголовком Upgrade: websocket, который указывает серверу желание переключиться с HTTP на WebSocket-протокол.
  • Заголовок Connection: Upgrade подтверждает намерение перейти на другой протокол.
  • В заголовке Sec-WebSocket-Key клиент отправляет случайную строку, которая будет использоваться сервером для создания уникального ключа для данного соединения.
  • Сервер отвечает с кодом статуса 101 Switching Protocols, если поддерживает WebSocket. Далее включает заголовок Sec-WebSocket-Accept, в котором находится подтвержденный ключ.
  • После успешного Handshake соединение переходит в WebSocket-протокол. Клиент и сервер теперь могут обмениваться данными в рамках постоянного соединения.

Запрос от клиента:

			GET /chat HTTP/1.1 // клиент запрашивает путь /chat с использованием HTTP 1.1
Host: example.com // домен сервера, к которому клиент пытается подключиться
Upgrade: websocket // указывает, что клиент хочет перейти с HTTP на WebSocket
Connection: Upgrade // подтверждает намерение установить соединение WebSocket
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== // случайно сгенерированный ключ, уникальный для каждого соединения. Сервер использует его для создания подтверждающего ключа, который проверит запрос
Sec-WebSocket-Version: 13 // версия WebSocket-протокола. Веб-браузеры и серверы обычно поддерживают версию 13
		

Ответ сервера:

			HTTP/1.1 101 Switching Protocols // статус 101 указывает, что сервер согласен переключиться на WebSocket-протокол
Upgrade: websocket // сервер подтверждает переход на WebSocket-протокол
Connection: Upgrade // подтверждение установления соединения WebSocket
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= // серверный ответный ключ, который рассчитывается путем шифрования Sec-WebSocket-Key, переданного клиентом, с помощью алгоритма SHA-1 и кодирования в Base64. Это подтверждает клиенту, что сервер принимает запрос WebSocket и согласен на соединение
		

Ключ безопасности в заголовке Sec-WebSocket-Accept генерируется сервером на основе ключа клиента Sec-WebSocket-Key с использованием следующего алгоритма:

  1. Сервер берет значение Sec-WebSocket-Key, добавляет к нему фиксированную строку "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  2. Результат хэшируется с помощью SHA-1
  3. Полученный хэш преобразуется в формат Base64 и отправляется клиенту в заголовке Sec-WebSocket-Accept

Передача данных

После установки соединения WebSocket данные передаются в виде фреймов — компактных порций данных, которые могут быть отправлены в любом направлении. Фреймы WebSocket минимальны по размеру, что позволяет сократить объем передаваемых данных и уменьшить задержку.

  • Фреймы данных: каждый фрейм включает служебную информацию (например, флаги начала и конца сообщения), а также полезную нагрузку (payload), содержащую данные.
  • Типы фреймов: WebSocket поддерживает несколько типов фреймов, включая текстовые (обычно в формате JSON) и бинарные (например, для передачи изображений или других медиаданных), что позволяет передавать данные любого формата.
  • Фреймы управления: используются для закрытия соединения, проверки активности соединения (ping/pong), а также других служебных задач.

Принципы работы SSE

Установка соединения SSE через HTTP/2

Рассмотрим установку соединения HTTP/2, поскольку эта версия наиболее популярная. У нас есть веб-приложение, которое должно получать уведомления о состоянии сервера в режиме реального времени. Клиент инициирует подключение с сервером, чтобы подписаться на эти события. При использовании HTTP/2 устанавливается одно TCP-соединение для всего обмена данными между клиентом и сервером, включая как SSE-события, так и обычные HTTP-запросы.

Запрос клиента:

			GET /events HTTP/2
Host: example.com
Accept: text/event-stream
		
  • Клиент отправляет обычный GET-запрос на сервер, чтобы подписаться на поток событий (с заголовком Accept: text/event-stream, указывающим, что он ожидает поток SSE).
  • HTTP/2 позволяет использовать одно TCP-соединение для всех взаимодействий клиента с сервером, что исключает необходимость в открытии новых соединений и снижает нагрузку на сеть.

Ответ сервера и поток данных в формате SSE

Сервер принимает запрос и начинает отправлять данные в формате text/event-stream. Благодаря мультиплексированию HTTP/2 сервер может отправлять этот поток данных одновременно с другими ресурсами (CSS, JavaScript), используя одно и то же соединение.

Ответ сервера:

			HTTP/2 200
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
		

После этого сервер начинает отправлять события в формате SSE, например, обновления состояния сервера или уведомления о новых событиях.

Формат события SSE

События SSE передаются в виде текста, где каждое событие завершается пустой строкой для разделения сообщений. Вот пример данных, которые сервер может отправлять клиенту:

			event: status
id: 101
data: {"cpu": "65%", "memory": "80%"}

event: alert
id: 102
data: {"type": "warning", "message": "Высокая нагрузка на сервер"}

event: status
id: 103
data: {"cpu": "70%", "memory": "85%"}

		

Управление соединением и автоматическое переподключение

Если соединение разрывается, браузер автоматически попытается переподключиться. В HTTP/2 переподключение происходит быстрее, так как открывается всего одно TCP-соединение, которое поддерживает все потоки данных, включая SSE.

Пример с поддержкой Last-Event-ID:

  • Клиент может отправить заголовок Last-Event-ID при переподключении, чтобы сообщить серверу, на каком событии он остановился.
  • Сервер может продолжить отправку событий с нужного ID, что предотвращает потерю данных при разрыве соединения. Данные будут отправляться с нужного события.

Запрос клиента на переподключение с Last-Event-ID

			GET /events HTTP/2
Host: example.com
Accept: text/event-stream
Last-Event-ID: 103
		

Производительность

1. WebSocket и высокая нагрузка

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

Влияние на производительность и потребление ресурсов

  • Использование памяти и CPU. Поскольку WebSockets поддерживают постоянные соединения, каждый подключенный клиент занимает ресурсы сервера (память, процессорное время, сокеты).
  • Потребление сетевых ресурсов. WebSocket соединения могут передавать данные часто и с высокой скоростью, что увеличивает сетевой трафик. Кроме того, WebSockets передают ping/pong пакеты для проверки активности соединения, что также требует ресурсов.
  • Энергопотребление на мобильных устройствах. Постоянное соединение и активный обмен данными приводят к увеличению энергопотребления на устройствах пользователей, особенно на мобильных, так как устройство должно часто выходить в сеть для поддержания соединения.

2. SSE и высокая нагрузка

SSE — это однонаправленный протокол, который отправляет данные от сервера к клиенту по открытому HTTP-соединению.

Влияние на производительность и потребление ресурсов

  • Меньше потребление памяти и CPU. SSE поддерживает только однонаправленное соединение, что снижает нагрузку на процессор и память по сравнению с WebSockets. 
  • Оптимизированная работа в сетях. SSE использует HTTP и работает на стандартных портах, что облегчает работу через прокси и балансировщики нагрузки и позволяет эффективнее использовать сетевые ресурсы.
  • Энергопотребление. SSE менее требователен к батарее на мобильных устройствах, так как поддерживает постоянное соединение без дополнительных проверок активности (например, ping/pong).

Когда лучше использовать WebSockets, а когда SSE?

Выбор между WebSockets и Server-Sent Events (SSE) зависит от требований конкретного приложения и от того, как данные должны передаваться между клиентом и сервером.

Когда лучше использовать WebSockets

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

  1. Приложения с высокой интерактивностью и двусторонней связью. WebSockets идеально подходят для чатов, игр, и финансовых платформ, где пользователи взаимодействуют друг с другом и отправляют данные на сервер с минимальной задержкой.
  2. Сценарии с частыми и неограниченными обновлениями. WebSockets поддерживают постоянное двустороннее соединение, что позволяет обмениваться данными в любой момент.
  3. Сложные интерфейсы с большим объемом данных. Если приложение постоянно получает или отправляет данные в обе стороны (платформа для мониторинга в реальном времени с большим количеством пользователей).
  4. Работа с бинарными данными. WebSockets поддерживают как текстовые, так и бинарные данные, что делает их подходящими для мультимедийных приложений, передающих изображения, видео или файлы.
  5. Низкая задержка и критичность времени отклика. В сценариях, где задержка в миллисекунды может влиять на пользовательский опыт, например, в торговых платформах, WebSockets позволяют передавать данные в реальном времени с минимальной задержкой.

Когда лучше использовать Server-Sent Events (SSE)

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

  1. Приложения с однонаправленным потоком данных от сервера к клиенту. SSE хорошо подходит для лент новостей, уведомлений, ценовых обновлений и других данных, которые сервер отправляет клиенту без необходимости получать ответы.
  2. Сценарии с меньшими требованиями к частоте обновления. Если данные обновляются с предсказуемой или средней частотой, SSE достаточно для поддержания потока.
  3. Поддержка автоматического переподключения и восстановления после разрывов. SSE автоматически восстанавливает соединение, если оно разрывается, и не требует сложной логики на стороне клиента.
  4. Энергосбережение на мобильных устройствах. SSE менее требователен к энергии на мобильных устройствах, чем WebSockets, так как оно не требует постоянного пинга для поддержания соединения.
  5. Поддержка стандартных сетей и кэширования. SSE работает поверх HTTP и использует текстовые данные, что упрощает поддержку в корпоративных сетях и через прокси-серверы.
SSE vs WebSockets: разобрались, что где лучше работает 8

WebSocket и SSE: собеседования по проектированию

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

Рассмотрим несколько кейсов на собеседованиях

Объясните, как бы вы спроектировали архитектуру для системы уведомлений/чата/игры с помощью WebSocket или SSE.

Контекст: Интервьюер может попросить кандидата представить архитектуру для приложения, требующего передачи данных в реальном времени, и объяснить выбор между WebSocket и SSE.

Пример ответа:

  • Для системы чата я выбрал бы WebSocket, так как требуется двусторонняя связь. Я бы организовал кластер серверов с балансировщиком нагрузки, поддерживающим постоянные соединения. Для синхронизации сообщений между серверами я бы использовал Redis как брокер сообщений.
  • Для системы уведомлений я бы выбрал SSE, так как требуется только передача данных от сервера к клиенту. Используя HTTP/2, можно мультиплексировать несколько потоков на одном соединении.
Как WebSocket и SSE ведут себя в условиях плохого интернет-соединения?

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

Примеры ответа:

  • WebSocket требует ручного переподключения при разрыве связи, поэтому для устойчивости можно реализовать автоматическое переподключение с сохранением состояния. Это добавляет сложности к архитектуре, но необходимо для обеспечения стабильной работы.
  • SSE автоматически переподключается при разрыве, и клиент может использовать Last-Event-ID, чтобы сервер мог продолжить отправку событий с последнего места.
Как выбрать WebSocket или SSE в зависимости от требований производительности и ресурсов?

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

Пример ответа:

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

Архитектурные примеры для реальных проектов

Пример для WebSocket:

  • Чат-приложение: Архитектура включает WebSocket-сервер, работающий с Redis для синхронизации сообщений между узлами. Каждый узел поддерживает постоянные соединения с клиентами. Для балансировки нагрузки используются специализированные балансировщики, такие как Nginx или HAProxy, а для хранения сообщений — базы данных с поддержкой очередей (например, RabbitMQ или Redis).

Пример для SSE:

  • Новостное приложение: Архитектура включает HTTP/2-сервер для поддержки SSE и отправки обновлений новостей. Сервер поддерживает CORS и отправляет данные только авторизованным пользователям. Для масштабирования используется балансировщик нагрузки, а каждое соединение работает через HTTP/2, что позволяет передавать данные без дополнительных соединений.
WebSockets и SSE — это одни из ключевых технологий для работы с данными в реальном времени. WebSockets подходят для сложных, интерактивных приложений с двусторонней связью, таких как чаты или игры, тогда как SSE — отличный выбор для простых потоков данных от сервера к клиенту, например, уведомлений или лент новостей. Понимание их различий поможет выбрать подходящее решение и оптимизировать ваш проект.
Следите за новыми постами
Следите за новыми постами по любимым темам
221 открытий2К показов