Что такое WebSocket и когда использовать вместо HTTP polling?

SeniorNode.js · Backend·Обновлено 18 июня 2026
Коротко
WebSocket — это полнодуплексный протокол поверх TCP, дающий постоянное соединение между клиентом и сервером. Его берут вместо HTTP polling, когда нужна низкая задержка и push-сообщения от сервера: чаты, котировки, нотификации, совместное редактирование.

Что такое WebSocket

WebSocket (RFC 6455) — это прикладной протокол поверх TCP, который начинается с HTTP-запроса с заголовком Upgrade: websocket и после успешного handshake переходит в двунаправленный (full-duplex) бинарный/текстовый канал. После апгрейда HTTP больше не используется: обе стороны шлют кадры (frames) в любой момент, без запроса-ответа.

Ключевые свойства:

  • одно долгоживущее TCP-соединение вместо потока коротких HTTP-запросов;
  • сервер может инициировать отправку (server push) без запроса клиента;
  • маленький оверхед на сообщение — кадр от 2 байт против сотен байт HTTP-заголовков;
  • работает поверх 80/443, дружит с прокси и TLS (wss://).

HTTP polling и его варианты

  • Short polling — клиент по таймеру шлёт GET /messages. Простой, но создаёт RPS-нагрузку и задержку до интервала опроса.
  • Long polling — сервер держит запрос открытым, пока не появятся данные, потом отвечает. Лучше по задержке, но всё равно одно сообщение на один HTTP-цикл и постоянные реконнекты.
  • SSE (Server-Sent Events) — однонаправленный push сервер → клиент поверх HTTP. Хорош для лент новостей, но не даёт канал клиент → сервер.

Когда WebSocket выигрывает

Брать WebSocket, когда выполняется хотя бы одно:

  • нужна низкая задержка двусторонняя связь (чаты, игры, совместное редактирование, WebRTC-сигналинг);
  • сервер часто инициирует сообщения, а интервал опроса пришлось бы делать <1–2 с;
  • много мелких частых сообщений — оверхед HTTP-заголовков становится дороже полезной нагрузки;
  • нужны подписки на несколько потоков по одному соединению (биржевые тикеры, телеметрия).

Когда WebSocket не нужен

  • Запрос-ответ без push — обычный REST/HTTP/2 проще и кэшируется.
  • Только сервер → клиент, редкие события — SSE дешевле и переживает обрывы из коробки.
  • Запросы раз в минуту и реже — short polling достаточен и не требует stateful-инфраструктуры.

Цена WebSocket в Node.js

  • Соединение stateful: балансировщик должен делать sticky sessions или использовать общий брокер (Redis pub/sub, NATS) для масштабирования между инстансами.
  • Нужны heartbeat (ping/pong) и автоматический reconnect на клиенте — TCP сам обрыв не всегда замечает.
  • Авторизация делается на handshake (cookie/Sec-WebSocket-Protocol/токен в первом сообщении), потому что заголовки потом не приходят.
  • На каждое соединение тратится память и файловый дескриптор — десятки тысяч сокетов требуют тюнинга ulimit и backpressure.

Короткое правило выбора

Если задержка важнее простоты и поток сообщений двусторонний — WebSocket. Если поток односторонний от сервера — SSE. Если события редкие или нужен кэш/CDN — обычный HTTP с long polling в худшем случае.

Что хочет услышать интервьюер

Понимание, что WebSocket — full-duplex поверх TCP с HTTP-handshake и Upgrade

Сравнение short polling, long polling, SSE и WebSocket по задержке и оверхеду

Конкретные кейсы применения: чаты, игры, тикеры, совместное редактирование

Осознание stateful-природы и проблем масштабирования (sticky sessions, pub/sub)

Знание про heartbeat, reconnect и авторизацию на handshake

Пример: WebSocket-сервер на ws с heartbeat

import { WebSocketServer, WebSocket } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

// Heartbeat: помечаем живые соединения и выбрасываем мёртвые
function heartbeat(this: WebSocket & { isAlive?: boolean }) {
  this.isAlive = true;
}

wss.on('connection', (ws: WebSocket & { isAlive?: boolean }, req) => {
  // Авторизация на handshake — токен в query или заголовке
  const token = new URL(req.url ?? '/', 'http://x').searchParams.get('token');
  if (!token) return ws.close(4001, 'unauthorized');

  ws.isAlive = true;
  ws.on('pong', heartbeat);

  ws.on('message', (data) => {
    // Эхо, в реальном коде — роутинг по типу события
    ws.send(data);
  });
});

// Раз в 30 секунд проверяем живость и пингуем
const interval = setInterval(() => {
  for (const client of wss.clients as Set<WebSocket & { isAlive?: boolean }>) {
    if (client.isAlive === false) {
      client.terminate();
      continue;
    }
    client.isAlive = false;
    client.ping();
  }
}, 30_000);

wss.on('close', () => clearInterval(interval));

Пример: Клиент с reconnect и экспоненциальным backoff

function connect(url: string) {
  let attempt = 0;
  let ws: WebSocket;

  const open = () => {
    ws = new WebSocket(url);

    ws.onopen = () => {
      attempt = 0; // Сброс backoff после успешного коннекта
    };

    ws.onmessage = (e) => {
      // Обработка сообщения от сервера
      console.log('msg', e.data);
    };

    ws.onclose = () => {
      // Экспоненциальный backoff с потолком 30 секунд
      const delay = Math.min(30_000, 1000 * 2 ** attempt++);
      setTimeout(open, delay);
    };

    ws.onerror = () => ws.close();
  };

  open();
  return () => ws?.close();
}

const stop = connect('wss://example.com/feed?token=abc');

Пример: Long polling для сравнения

// Клиентский long polling — каждый ответ закрывает HTTP и открывает новый
async function longPoll(cursor = 0): Promise<void> {
  try {
    const res = await fetch(`/api/messages?since=${cursor}`);
    const { messages, nextCursor } = await res.json();
    for (const m of messages) handle(m);
    return longPoll(nextCursor);
  } catch {
    // На ошибке — пауза и повтор, чтобы не положить сервер
    await new Promise((r) => setTimeout(r, 2000));
    return longPoll(cursor);
  }
}

function handle(_m: unknown) {}

Типичные ошибки

Считать WebSocket «улучшенным HTTP» и тянуть его на любой запрос-ответ

Игнорировать SSE и предлагать WebSocket там, где нужен только server → client поток

Забывать про sticky sessions/общий брокер при горизонтальном масштабировании

Не реализовывать ping/pong и reconnect, надеясь, что TCP сам отловит обрыв

Передавать авторизацию в каждом сообщении вместо проверки на handshake

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее