Розробка Server-Sent Events (SSE) для веб-додатку

Коли дані на сайті застарівають, користувачам доводиться оновлювати сторінку вручну або системі — опитувати сервер десятки разів на хвилину. Це перевантажує інфраструктуру та псує враження від сервісу. Ми розробляємо Server-Sent Events (SSE) — технологію, яка доставляє оновлення миттєво, у реальному часі, без зайвих запитів. Наша команда реалізує рішення під ключ, забезпечуючи стабільну роботу та підтримку на всіх етапах.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Розробка Server-Sent Events (SSE) для веб-додатку
Середній
від 1 дня до 3 днів
Часті запитання

При оновленні статусу замовлення клієнту доводиться перезавантажувати сторінку або тягнути API кожні 5 секунд. Це збільшує навантаження на сервер і погіршує UX. Аналогічна ситуація — на фінансовій платформі, де котирування змінюються щосекунди, але WebSocket надлишковий, а постійні полінги вбивають базу даних. Ми вирішуємо цю проблему за допомогою Server-Sent Events (SSE) — технології односпрямованої потокової передачі даних від сервера до клієнта через HTTP. Наш досвід показує: SSE знижує кількість запитів у 10 разів і забезпечує доставку подій за <1 с.

Чому SSE, а не WebSocket?

Критерій SSE WebSocket
Напрямок Односторонній (сервер→клієнт) Двосторонній
HTTP-сумісність Нативний HTTP/1.1 Вимагає upgrade
Автоперепідключення Вбудоване Потрібно реалізовувати
Робота через проксі Без проблем Часто блокується
CORS GET без preflight Preflight для не-GET

SSE краще підходить для завдань, де важливі сповіщення та live-оновлення. Відправка даних від клієнта рідкісна. Налаштування SSE в середньому займає вдвічі менше часу, ніж WebSocket. Наприклад, для сповіщень про статус замовлення SSE ідеальний, а для чату — WebSocket.

Як SSE масштабується на кілька серверів?

В одному проекті для великого e-commerce ми реалізували SSE з розподіленою доставкою через Redis Pub/Sub. Клієнт отримав зменшення часу оновлення статусу замовлення з 5 хвилин до 1 секунди. При цьому система витримує 10 000 одночасних підключень без втрати подій. Масштабування досягається за рахунок брокера повідомлень: всі сервери підписуються на загальний канал і публікують події для всіх клієнтів. Ми використовуємо цей патерн у більш ніж 30 проектах.

Технічна реалізація

Формат SSE

Відповідь сервера — text/event-stream з полями data:, event:, id:, retry::

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

data: {"type":"notification","message":"Новий заказ"}

event: order_update
data: {"orderId":"123","status":"shipped"}
id: msg_456
retry: 3000

: comment (ігнорується клієнтом)

Серверний endpoint (Node.js + Express)

Повний код серверного endpoint з heartbeat та реєстром клієнтів
app.get('/api/events', (req, res) => {
  const userId = req.user.id;
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    'X-Accel-Buffering': 'no', // для Nginx: отключить буферизацию
  });
  // Немедленно отправляем первый пакет (обход nginx буферизации)
  res.write(':ok\n\n');
  // Добавляем клиента в реестр
  const clientId = nanoid();
  clients.set(clientId, { res, userId });
  // Heartbeat каждые 15 секунд
  const heartbeat = setInterval(() => {
    res.write(': ping\n\n');
  }, 15000);
  req.on('close', () => {
    clearInterval(heartbeat);
    clients.delete(clientId);
  });
});

// Отправка события конкретному пользователю
function sendToUser(userId: string, event: string, data: object) {
  clients.forEach(({ res, userId: uid }) => {
    if (uid === userId) {
      res.write(`event: ${event}\n`);
      res.write(`data: ${JSON.stringify(data)}\n\n`);
    }
  });
}

// Broadcast всем
function broadcast(event: string, data: object) {
  clients.forEach(({ res }) => {
    res.write(`event: ${event}\n`);
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  });
}

Клієнт на EventSource

Підключення через EventSource з підтримкою іменованих подій:

const eventSource = new EventSource('/api/events', { withCredentials: true });

// Дефолтные события (event: без имени)
eventSource.onmessage = (e) => {
    const data = JSON.parse(e.data);
    console.log('Message:', data);
};

// Именованные события
eventSource.addEventListener('order_update', (e) => {
    const order = JSON.parse(e.data);
    updateOrderStatus(order.orderId, order.status);
});

eventSource.addEventListener('notification', (e) => {
    showNotification(JSON.parse(e.data).message);
});

// Обработка ошибок
eventSource.onerror = (e) => {
    if (eventSource.readyState === EventSource.CLOSED) {
        console.log('Соединение закрыто, автоматически переподключается...');
    }
};

Докладніше про EventSource можна прочитати в офіційній документації: MDN Web Docs.

Масштабування з Redis Pub/Sub

sub.subscribe('user:events', (message) => {
  const { userId, event, data } = JSON.parse(message);
  sendToUser(userId, event, data);
});

// Из любого сервиса
await pub.publish('user:events', JSON.stringify({
  userId: 'user_123',
  event: 'payment_completed',
  data: { amount: 5000 }
}));

Приклади іменованих подій

Подія Формат даних
notification {"type":"success","message":"..."}
order_update {"orderId":"123","status":"shipped"}
payment_completed {"amount":5000,"currency":"USD"}

Реальний кейс: SSE для фінансової платформи

Для стартапу, що надає дані про криптовалюти в реальному часі, ми спроектували систему на SSE. Вимагалося відправляти котирування 50+ валютних пар тисячам користувачів без затримок і без навантаження на клієнтські бази. Рішення: кожен користувач підписується на потрібні пари через кастомний endpoint, а сервери об'єднують події за протоколом Redis Pub/Sub. У підсумку — затримка доставки не більше 200 мс навіть при 10 000 підключень. Це дозволило відмовитися від WebSocket, спростити інфраструктуру і заощадити на розробці. Гарантуємо стабільність такої схеми під високим навантаженням.

Як забезпечити надійне перепідключення?

SSE автоматично перепідключається, але важливо налаштувати heartbeat. Якщо сервер не відправляє дані 30 секунд, браузер може закрити з'єднання. Тому ми відправляємо порожній коментар (: ping) кожні 15 секунд. Це підтримує з'єднання живим. Крім того, використовуйте заголовок X-Accel-Buffering: no для Nginx, щоб дані не буферизувалися.

Обмеження SSE

  • Тільки сервер → клієнт — клієнт не може відправити дані через SSE (тільки нові EventSource-запити або окремий API-запит).
  • Ліміт з'єднань в HTTP/1.1 — браузер обмежує 6 з'єднань на домен. SSE займає одне. Рішення: HTTP/2 (один мультиплексований потік).
  • IE не підтримує — поліфіл eventsource для старих браузерів.

Що входить в роботу і типові помилки

  • Проектування схеми подій і типів даних.
  • Реалізація серверного endpoint з аутентифікацією і heartbeat.
  • Клієнтська інтеграція (React hook, Angular service або нативний EventSource).
  • Навантажувальне тестування до 10 000 підключень.
  • Документування протоколу і коду.
  • Навчання команди замовника.

Типові помилки при впровадженні SSE:

  • Не враховують буферизацію Nginx — забувають заголовок X-Accel-Buffering: no.
  • Використовують SSE для двостороннього зв'язку — правильніше WebSocket.
  • Не ставлять heartbeat — з'єднання може розірватися і не відновитися.

Рядки

SSE-ендпоінт з аутентифікацією, heartbeat, Redis-scaling: 3–5 днів. З типізованими подіями, client-side хуком (useSSE), інтеграцією сповіщень: 1–2 тижні.

Оцінимо ваш проект — зв'яжіться з нами для консультації. Ми реалізували безліч рішень на SSE, гарантуємо стабільність під високим навантаженням. Якщо вам потрібно впровадити live-оновлення — отримайте консультацію.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060