В прошлом году я руководила проектом по созданию дашборда мониторинга в реальном времени для торговой платформы. Требование: визуализировать рыночные данные, обновляющиеся с частотой 1 миллион событий в секунду с задержкой до экранов пользователей менее 100 мс.
Это сломало все, что мы думали, что знаем о визуализации данных.
Реальность реального времени
Вот что никто вам не говорит: «реальное время» обычно не является реальным временем. И это часто нормально.
Большинство дашбордов с пометкой «реальное время» на самом деле обновляются каждые 5-30 секунд. Для большинства случаев использования этого вполне достаточно. Но когда вам действительно нужны обновления с субсекундной задержкой, правила меняются полностью.
Три уровня «реального времени»
Уровень 1: Почти реальное время (обновление каждые 5-60 секунд)
Случаи использования: Бизнес-дашборды, маркетинговая аналитика, метрики продаж
Архитектура: Опрос API-эндпоинтов, пакетная агрегация
Сложность: Умеренная
Это то, что нужно большинству людей. Команда данных агрегирует данные каждую минуту, дашборд опрашивает обновления. Просто и эффективно.
Уровень 2: Реальное время (обновление каждые 1-5 секунд)
Случаи использования: Мониторинг операций, отслеживание живых событий, очереди поддержки клиентов
Архитектура: WebSockets, server-sent events, потоковые запросы
Сложность: Высокая
Переход от опроса к принудительной отправке меняет все. Теперь вы поддерживаете постоянные соединения, обрабатываете логику повторного подключения и управляете состоянием между клиентом и сервером.
Уровень 3: Субсекундное (менее 1 секунды)
Случаи использования: Торговые платформы, статистика живых игр, промышленный мониторинг
Архитектура: Потоковые конвейеры, специализированные базы данных, оптимизированный рендеринг
Сложность: Очень высокая
Здесь мы жили 8 месяцев. Каждая оптимизация имеет значение. Каждая миллисекунда на счету.
Почему визуализация в реальном времени сложна
Проблема 1: Объем данных
При 1 млн событий/сек нельзя отображать каждое событие. Это один миллион точек каждую секунду. Браузер взорвется.
Решение: Предварительная агрегация. Не отправляйте сырые события на фронтенд. Агрегируйте у источника — средние значения, подсчеты, процентили на временной интервал. Мы отправляли 10 агрегированных обновлений в секунду вместо 1 000 000 сырых событий.
Проблема 2: Производительность рендеринга
Даже с 10 обновлениями в секунду повторный рендеринг целых графиков убивает производительность. Согласование React, манипуляции SVG, перерисовка canvas — все это суммируется.
Решение: Инкрементальные обновления. Не перестраивайте график; добавляйте к нему. Мы использовали рендеринг на основе WebGL для графиков с наивысшей частотой, которые могут плавно обрабатывать обновления с частотой 60 кадров в секунду.
Проблема 3: Человеческое восприятие
Вот контринтуитивный вывод: обновления быстрее ~200 мс становятся размытием. Пользователи не могут обрабатывать информацию с частотой 10+ кадров в секунду. Они просто видят мерцание.
Решение: Визуальное сглаживание. Даже когда данные обновляются с частотой 10 Гц, мы анимировали переходы в течение 200 мс. График ощущался «живым», не ощущаясь хаотичным.
Проблема 4: Изменчивость сети
Соединения WebSocket обрываются. Пакеты задерживаются. Мобильные пользователи переключают сети.
Решение: Надежное повторное подключение, очередь сообщений и изящная деградация. Если соединение прерывается, показывайте последнее известное состояние с индикатором «переподключение» — не показывайте пустой экран.
Архитектура, которая сработала
Вот стек, который справился с нашим требованием в миллион событий в секунду:
Слой данных
- Apache Kafka для приема событий
- Apache Flink для агрегации в реальном времени
- Redis для кэширования последнего состояния
- TimescaleDB для исторических запросов
Слой API
- Go для WebSocket-сервера (эффективно обрабатывает одновременные соединения)
- gRPC для внутренней коммуникации сервисов
- Пакетирование сообщений (отправлять обновления каждые 100 мс, а не каждое событие)
Фронтенд
- React для структуры UI
- WebGL (через regl) для высокочастотных графиков
- Легковесный canvas для среднечастотных графиков
- SVG (через D3) только для низкочастотных, интерактивных графиков
Ключевые решения
- Агрегировать как можно раньше: Фронтенд должен получать готовые к отображению данные, не сырые события.
- Разделить частоты обновления: Не каждому элементу нужны 10 кадров в секунду. Статический контекст может обновляться каждые 30 секунд.
- Пользовательский контроль уровня детализации: Пусть пользователи выбирают между «обзором» (медленнее обновления, больше данных) и «детализированным» (быстрее обновления, сфокусированный вид).
Оптимизации производительности
На сервере
- Предварительно вычислять временные интервалы: Не заставляйте клиента вычислять «последние 5 минут»
- Дельта-кодирование: Отправлять только то, что изменилось, не полное состояние
- Сжатие: gzip сообщений WebSocket (удивительно эффективно)
- Пул соединений: Повторно использовать соединения между подписками
На клиенте
- Пул объектов: Повторно использовать элементы графиков вместо сборки мусора
- Пакетирование RequestAnimationFrame: Синхронизировать обновления с циклом рендеринга браузера
- Слои canvas: Статические элементы на одном canvas, динамические на другом
- Web Workers: Парсить входящие данные вне основного потока
Что не сработало
- SVG для высокочастотных обновлений (манипуляции с DOM слишком медленные)
- Redux для состояния в реальном времени (слишком много накладных расходов для частых обновлений)
- Готовые библиотеки графиков для >5 кадров в секунду (не оптимизированы для этого случая использования)
Уроки UX из реального времени
Урок 1: Дайте пользователям контроль
Не все хотят живые обновления. Некоторых они отвлекают. Мы добавили:
- Кнопку паузы: «Заморозить» текущий вид
- Селектор частоты обновления: 1 секунда, 5 секунд, 30 секунд
- Исторический режим: «Покажите мне, что произошло 5 минут назад»
Урок 2: Сделайте состояние очевидным
Пользователям нужно знать:
- Это живое или историческое?
- Когда было последнее обновление?
- Здорово ли соединение?
Мы добавили постоянный индикатор «сердцебиения», который пульсировал с каждым обновлением. Удивительно успокаивающе.
Урок 3: Обрабатывайте скучные состояния
Большую часть времени ничего интересного не происходит. График просто... обновляется похожими значениями.
Вот где помогают аннотации: «Пик обнаружен в 14:32» привлекает внимание к значимым изменениям. Без этого пользователи смотрят на шум.
Урок 4: Мобильные устройства — другие
Меньшие экраны, худшие соединения, ограничения батареи. Для мобильных:
- Автоматически уменьшайте частоту обновления
- Упрощайте визуализации
- Добавляйте агрессивную логику повторного подключения
Когда реальное время того не стоит
После создания этой системы я более скептически отношусь к требованиям реального времени. Спросите:
- Будут ли более быстрые обновления менять поведение пользователей?
- Могут ли пользователи реально действовать на информацию так быстро?
- Оправданы ли инженерные затраты?
Для большинства дашбордов ответ — нет. Обновление каждые 15 секунд — нормально. Оставьте реальное время для случаев, когда секунды действительно имеют значение.
Инструменты и ресурсы
Для уровней 1-2 (почти реальное время и реальное время):
Современные инструменты, такие как ChartGen, могут генерировать графики, которые эффективно опрашивают API. В сочетании с WebSocket-эндпоинтами можно создавать надежные дашборды реального времени без пользовательской инфраструктуры.
Для уровня 3 (субсекундное):
Вам понадобятся специализированные инструменты: D3 с canvas, пользовательский WebGL-рендеринг или предназначенные библиотеки, такие как uPlot или Apache ECharts с режимом инкрементального обновления.
Для потоковой инфраструктуры:
- Apache Kafka + Flink (сложно, но мощно)
- AWS Kinesis + Lambda (управляемо, но ограничено)
- Redis Streams + пользовательская агрегация (проще, но менее масштабируемо)
Мониторинг вашей системы реального времени
Что мы измеряли:
- Сквозная задержка (временная метка события до пикселя на экране)
- Здоровье соединения (повторные подключения в час)
- Производительность рендеринга (кадры в секунду)
- Вовлеченность пользователей (смотрят ли люди на вид реального времени на самом деле?)
Неожиданный вывод: Многие пользователи открывали дашборд, смотрели 2 минуты, затем оставляли его на фоновой вкладке. «Живые» данные в основном не просматривались.
Заключительная мысль
Визуализация в реальном времени — это инженерная задача, но также и UX-задача. Самое сложное — не доставить данные на экран быстро — а представить их так, чтобы люди могли реально понять и действовать на их основе.
Прежде чем создавать реальное время, спросите: что пользователи будут делать по-другому с более быстрыми данными?
Если ответ не ясен, возможно, вам вообще не нужно реальное время.


