Типичная ситуация: дашборд на React обновляется только после нажатия F5. Менеджеры теряют до 15% конверсии из-за устаревших статусов заказов. При задержке в 5 секунд потери могут достигать 8% выручки. Мы разрабатываем дашборды с live-обновлением через WebSocket или SSE — данные обновляются мгновенно после изменения на сервере. В своей практике мы добились времени доставки обновления 50 мс при 1000 одновременных подключениях и стабильности 99.9%. Запишитесь на бесплатную диагностику вашего текущего решения для оценки потенциальной экономии.
Как выбрать между WebSocket и SSE?
WebSocket — двунаправленный, подходит для интерактивных дашбордов с фильтрами. SSE — однонаправленный, проще, работает через HTTP/2, автоматически переподключается. Для пассивного наблюдения за метриками 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%.
Почему нам доверяют?
Более 5 лет разрабатываем real-time решения для e-commerce и SaaS. В портфолио — 50+ проектов с дашбордами для крупных маркетплейсов и логистических систем. Гарантируем стабильность соединения (99.9% аптайма), обновление данных за 50 мс и снижение нагрузки на сервер до 30% за счёт оптимального выбора транспорта. Свяжитесь с нами для оценки вашего проекта — мы предложим архитектуру под ключ.







