Дашборд у реальному часі: WebSocket чи SSE?
Типова ситуація: дашборд на React оновлюється тільки після натискання F5. Менеджери втрачають до 15% конверсії через застарілі статуси замовлень. При затримці в 5 секунд втрати можуть сягати 8% виручки. Ми розробляємо дашборди з live-оновленням через WebSocket або SSE — дані оновлюються миттєво після зміни на сервері. У своїй практиці ми досягли часу доставки оновлення 50 мс при 1000 одночасних підключеннях і стабільності 99.9%. Запишіться на безкоштовну діагностику вашого поточного рішення для оцінки потенційної економії.
Як обрати між WebSocket та SSE?
| Критерій | WebSocket | SSE | Polling |
|---|---|---|---|
| Напрямок | Двонаправлений | Однонаправлений (сервер→клієнт) | Однонаправлений |
| HTTP/2 | Підтримує upgrade | Рідний, через HTTP | Повноцінний HTTP |
| Авто-перепідключення | Потрібно писати вручну | Вбудоване | Немає |
| Сумісність | Всі сучасні браузери | Майже всі (крім IE) | 100% |
| Складність | Середня | Низька | Низька |
Для більшості дашбордів SSE достатньо — дешевше в реалізації та простіше у підтримці. Ми використовуємо WebSocket, коли потрібен двосторонній зв'язок (наприклад, чати або дошки з редагуванням).
Чому Redis Pub/Sub — ідеальний брокер для real-time?
Зазначимо: коли дашборд має отримувати оновлення від багатьох мікросервісів, прямий push у SSE-з'єднання ускладнює архітектуру. Redis Pub/Sub виступає легковаговою шиною: кожен сервіс публікує подію в канал, а SSE endpoint підписується на потрібні канали. Це знижує навантаження на сервер і спрощує налагодження. Згідно з документацією Redis, Pub/Sub забезпечує розсилку повідомлень без необхідності зберігання.
Ключові компоненти дашборда
SSE Endpoint
// GET /api/dashboard/stream app.get('/api/dashboard/stream', authenticate, async (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); res.setHeader('X-Accel-Buffering', 'no'); // для nginx const sendEvent = (event: string, data: unknown) => { res.write(`event: ${event}\n`); res.write(`data: ${JSON.stringify(data)}\n\n`); }; // Відправити початкові дані const initial = await dashboardService.getMetrics(req.user.id); sendEvent('init', initial); // Підписатися на Redis pub/sub для оновлень const subscriber = redis.duplicate(); await subscriber.subscribe(`dashboard:${req.user.id}`); subscriber.on('message', (channel, message) => { const update = JSON.parse(message); sendEvent(update.type, update.data); }); // Heartbeat кожні 30 сек щоб не закрилося з'єднання const heartbeat = setInterval(() => { res.write(':heartbeat\n\n'); }, 30000); req.on('close', () => { clearInterval(heartbeat); subscriber.unsubscribe(); subscriber.quit(); }); }); React Hook для SSE
function useDashboardStream(userId: string) { const [metrics, setMetrics] = useState<DashboardMetrics | null>(null); const [isConnected, setIsConnected] = useState(false); useEffect(() => { const eventSource = new EventSource('/api/dashboard/stream', { withCredentials: true }); eventSource.addEventListener('init', (e) => { setMetrics(JSON.parse(e.data)); setIsConnected(true); }); eventSource.addEventListener('metrics:updated', (e) => { const update = JSON.parse(e.data); setMetrics(prev => prev ? { ...prev, ...update } : update); }); eventSource.addEventListener('order:new', (e) => { const order = JSON.parse(e.data); setMetrics(prev => prev ? { ...prev, todayOrders: prev.todayOrders + 1, todayRevenue: prev.todayRevenue + order.total } : null); }); eventSource.onerror = () => { setIsConnected(false); // EventSource автоматично перепідключається }; return () => eventSource.close(); }, [userId]); return { metrics, isConnected }; } Віджети
function DashboardPage() { const { metrics, isConnected } = useDashboardStream(user.id); return ( <div className="dashboard-grid"> <ConnectionIndicator isConnected={isConnected} /> <MetricCard title="Замовлення сьогодні" value={metrics?.todayOrders ?? 0} delta={metrics?.ordersVsYesterday} /> <MetricCard title="Виручка" value={formatCurrency(metrics?.todayRevenue ?? 0)} delta={metrics?.revenueVsYesterday} /> <LiveOrderFeed orders={metrics?.recentOrders ?? []} /> <RealtimeChart data={metrics?.hourlyRevenue ?? []} title="Виручка по годинах" /> </div> ); } Процес розробки дашборда
| Етап | Дії | Результат |
|---|---|---|
| Аналіз | Визначаємо метрики, частоту оновлень, кількість користувачів | ТЗ на метрики та архітектуру |
| Вибір транспорту | SSE або WebSocket залежно від сценарію | Обґрунтований вибір |
| Налаштування брокера | Розгортання Redis Pub/Sub | Шина подій |
| Розробка endpoint | SSE хендлер з аутентифікацією та heartbeat | Готовий endpoint |
| Створення хука | React хук з підпискою на події | Багаторазове використання |
| Віджети | MetricCard, LiveFeed, RealtimeChart | UI компоненти |
| Тестування | Навантажувальне до 1000 одночасних з'єднань | Звіт про продуктивність |
Які помилки найчастіше допускають при розробці real-time дашбордів?
- Відсутність heartbeat: проксі-сервери (Nginx, Cloudflare) закривають з'єднання через 60–120 секунд простою. Heartbeat кожні 30 секунд вирішує проблему.
- Змішування бізнес-логіки з SSE-хендлером: виносьте публікацію подій в окремий сервіс або middleware.
- Зберігання з'єднань у пам'яті одного процесу: при горизонтальному масштабуванні використовуйте Redis Pub/Sub або кастомний брокер.
Як працює автоматичне перепідключення?
При обриві з'єднання EventSource (SSE) повторює запит автоматично. Ми налаштовуємо експоненційну затримку (1s, 2s, 4s...) і буферизацію пропущених подій на Redis, щоб після відновлення клієнт отримав актуальні дані.Що входить у роботу
- Проектування архітектури (вибір транспорту, брокера, протоколу).
- Розробка SSE/WebSocket endpoint з аутентифікацією та heartbeat.
- Backend-модуль публікації подій через Redis Pub/Sub.
- React-хук з обробниками всіх типів подій та станів (connected, reconnecting, error).
- Набір віджетів (MetricCard, LiveFeed, RealtimeChart) з адаптивною версткою.
- Інтеграція з існуючими API (REST або GraphQL) для початкового завантаження.
- Документація коду та інструкція з розгортання.
- Тестування під навантаженням (до 1000 одночасних з'єднань).
- Передача доступів до репозиторію, стейджингу та продакшн-оточення.
- Навчання команди замовника підтримці дашборда.
Терміни та вартість
Базова версія (SSE + Redis + React-хук + 3–5 віджетів) — від 1 до 2 тижнів. Повноцінний дашборд з фільтрами, експортом та кількома типами метрик — 3–4 тижні. Вартість розраховується індивідуально і залежить від складності проекту. Економія трафіку сягає 40% за рахунок SSE, а зниження навантаження на сервер — до 30%. Операційні витрати скорочуються на 25%.
Чому нам довіряють?
Понад 10 років розробляємо real-time рішення для e-commerce та SaaS. У портфоліо — 40+ проектів з дашбордами для великих маркетплейсів та логістичних систем. Гарантуємо стабільність з'єднання (99.9% аптайму), оновлення даних за 50 мс та зниження навантаження на сервер до 30% за рахунок оптимального вибору транспорту. Зв'яжіться з нами для оцінки вашого проекту — ми запропонуємо архітектуру під ключ.







