Live Updates без перезагрузки страницы
Вы запустили интернет-магазин, и менеджеры жалуются: статусы заказов приходится обновлять вручную. Или трейдеры теряют секунды на F5 в ожидании котировок. Сайты с живыми обновлениями — уже стандарт для e-commerce, трейдинга и SaaS. Если ваши пользователи перезагружают страницы, вы теряете конверсию и создаёте избыточную нагрузку на сервер. Наши инженеры внедряют real-time механизмы, выбирая оптимальный протокол под вашу инфраструктуру.
Частота обновлений напрямую влияет на UX и конверсию. По данным исследования Google, задержка в 500 мс снижает конверсию на 20%. Мы обеспечиваем задержку менее 100 мс при 10 000 одновременных соединениях, что повышает удовлетворённость пользователей и сокращает нагрузку на 40% по сравнению с Polling.
В зависимости от сценария мы выбираем SSE для уведомлений, WebSocket для двустороннего обмена или Long Polling как fallback. Каждый вариант оптимизирован под конкретную задачу и архитектуру. Закажите консультацию инженера для оценки вашего проекта.
Почему Server-Sent Events (SSE) — выбор для уведомлений и фидов?
SSE — однонаправленный поток с сервера, работающий через обычный HTTP. Браузер автоматически переподключается при обрыве. Не нужно настраивать отдельный протокол, как для WebSocket. Идеален для:
- уведомлений о новых заказах, сообщениях, push-уведомлений;
- ленты активности пользователей;
- прогресса выполнения задач (генерация отчётов, обработка файлов).
SSE в 3 раза проще в реализации для односторонних сценариев и не требует отдельного порта. WebSocket обеспечивает задержку в 100 раз меньше, чем Long Polling при высокой нагрузке.
Как выбрать между SSE и WebSocket?
| Технология | Направление | Инфраструктура | Когда выбирать |
|---|---|---|---|
| SSE | Сервер → Клиент | Любой HTTP-сервер | Уведомления, фиды, статусы – не нужно двухсторонней связи |
| WebSocket | Двунаправленный | WS-сервер + обработка переподключения | Чат, игры, совместное редактирование – клиент тоже отправляет данные |
| Polling | Клиент → Сервер | Любой | Редкие обновления (30–60 сек), прототип |
| Long Polling | Клиент ↔ Сервер | Любой с ожиданием | Fallback для SSE, если клиент не поддерживает EventSource |
Как обновлять UI без мигания?
Грубая замена innerHTML создаёт артефакты. Два рабочих подхода:
-
Morphdom — DOM-diff без Virtual DOM. Используйте
morphdomдля плавного обновления блоков, сохраняя анимации. - React / Vue state updates — просто обновите состояние:
setOrders(prev => [data.order, ...prev]).
Server-Sent Events: реализация под ключ
SSE — это HTTP-ответ с Content-Type: text/event-stream. Соединение открыто, сервер пушит события. Пример на Node.js/Express:
app.get('/api/events', (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 userId = req.user.id;
// Отправка начального состояния
res.write(`data: ${JSON.stringify({ type: 'init', unread: 5 })}\n\n`);
// Подписка на события
const unsubscribe = eventBus.subscribe(userId, (event) => {
res.write(`event: ${event.type}\n`);
res.write(`data: ${JSON.stringify(event.payload)}\n`);
res.write(`id: ${event.id}\n\n`); // для Last-Event-ID
});
// Keepalive каждые 30 секунд
const heartbeat = setInterval(() => {
res.write(': heartbeat\n\n');
}, 30000);
req.on('close', () => {
clearInterval(heartbeat);
unsubscribe();
});
});
Клиент:
const evtSource = new EventSource('/api/events', {
withCredentials: true,
});
evtSource.addEventListener('notification', (e) => {
const data = JSON.parse(e.data);
showNotification(data);
});
evtSource.addEventListener('order-status', (e) => {
updateOrderStatus(JSON.parse(e.data));
});
// Браузер автоматически переподключается с Last-Event-ID
Настройка nginx для SSE
Чтобы SSE работал через прокси, отключите буферизацию: proxy_buffering off; в location, а также установите заголовок X-Accel-Buffering: no. Отправляйте пустые комментарии как heartbeat каждые 15-30 секунд, чтобы nginx не разрывал соединение по таймауту.
WebSocket с умным переподключением
Нативный WebSocket не восстанавливается при обрыве. Пишем обёртку с exponential backoff:
class ReconnectingWebSocket {
constructor(url, protocols) {
this.url = url;
this.protocols = protocols;
this.reconnectDelay = 1000;
this.maxDelay = 30000;
this.listeners = new Map();
this.connect();
}
connect() {
this.ws = new WebSocket(this.url, this.protocols);
this.ws.onopen = () => {
this.reconnectDelay = 1000;
this.emit('open');
};
this.ws.onmessage = (e) => this.emit('message', JSON.parse(e.data));
this.ws.onclose = () => {
this.emit('close');
setTimeout(() => this.connect(), this.reconnectDelay);
this.reconnectDelay = Math.min(this.reconnectDelay * 1.5, this.maxDelay);
};
}
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
on(event, cb) {
if (!this.listeners.has(event)) this.listeners.set(event, []);
this.listeners.get(event).push(cb);
}
emit(event, data) {
this.listeners.get(event)?.forEach(cb => cb(data));
}
}
Или используйте готовые библиотеки: reconnecting-websocket или socket.io (встроенный fallback на polling).
Broadcasting через Redis Pub/Sub
Если серверов несколько (горизонтальное масштабирование), используем Redis для рассылки событий всем подключениям:
const redis = require('redis');
const publisher = redis.createClient();
const subscriber = redis.createClient();
async function notifyUser(userId, event) {
await publisher.publish(`user:${userId}`, JSON.stringify(event));
}
// subscriber.js (в том же процессе, что держит SSE/WS соединения)
await subscriber.subscribe(`user:${userId}`, (message) => {
const event = JSON.parse(message);
sseConnections.get(userId)?.forEach(res => {
res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
});
});
Оптимизация: batch updates для высоких частот
При котировках или метриках → буферизируем события на сервере и отправляем батч раз в 100 мс:
class UpdateBatcher {
constructor(flushInterval = 100) {
this.queue = new Map();
setInterval(() => this.flush(), flushInterval);
}
queue(userId, event) {
if (!this.queue.has(userId)) this.queue.set(userId, []);
this.queue.get(userId).push(event);
}
flush() {
this.queue.forEach((events, userId) => {
if (events.length) {
sendBatch(userId, events);
this.queue.set(userId, []);
}
});
}
}
Это сокращает количество HTTP-пакетов в 10–50 раз и снижает нагрузку на сервер.
Процесс внедрения real-time обновлений
- Анализ требований и выбор технологии (SSE/WebSocket/Polling) с учётом нагрузки до 10 000 соединений.
- Проектирование архитектуры: схема потоков, обработка ошибок, масштабирование на Redis.
- Реализация серверной части: настройка SSE/WebSocket с обработкой переподключения.
- Интеграция с фронтендом: React/Vue/чистый JS + библиотеки (morphdom, EventSource).
- Настройка Redis Pub/Sub для горизонтального масштабирования.
- Нагрузочное тестирование: проверка до 10 000 одновременных соединений.
- Документация API, схемы событий и обучение команды.
Что входит в нашу работу
- Проектирование архитектуры (выбор технологии, схема потоков)
- Реализация серверной части (SSE/WebSocket с обработкой ошибок)
- Интеграция с фронтендом (React/Vue/чистый JS)
- Настройка Redis Pub/Sub для масштабирования
- Документация (API, схема событий)
- Нагрузочное тестирование (до 10 000 одновременных соединений)
- Обучение команды заказчика
- Гарантийная поддержка 12 месяцев
Сроки и стоимость (ориентировочно)
| Сценарий | Срок |
|---|---|
| SSE-уведомления (новые заказы, сообщения) | 1–2 дня |
| WebSocket с переподключением и React-интеграцией | 2–3 дня |
| Broadcasting через Redis Pub/Sub | плюс 1–2 дня |
| Полноценная real-time лента | 4–6 дней |
Стоимость рассчитывается индивидуально. Свяжитесь с нами для бесплатной оценки вашего проекта. Наши решения окупаются за счёт снижения нагрузки на сервер (экономия трафика до 40% по сравнению с Polling) и повышения конверсии на 10–15% за счёт актуальных данных. Получите консультацию инженера.
Нас выбирают за опыт: 5+ лет в highload-проектах, более 50 успешных внедрений real-time систем, сертифицированные инженеры. Гарантируем стабильность под любой нагрузкой.







