Real-time доставка без задержек
Представьте: вы запустили интернет-магазин, и клиенты жалуются, что уведомления о статусе заказа приходят с опозданием на 5–10 минут. Или админы не видят новые сообщения в чате поддержки, пока не перезагрузят страницу. Классический polling на AJAX убивает сервер и батарею мобильных устройств. Решение — Centrifugo, self-hosted real-time сервер на Go, который снижает нагрузку на бэкенд и экономит инфраструктурные затраты. Мы интегрировали его в 50+ проектах и гарантируем: после нашей настройки сообщения доставляются за <100 мс (TTFB), нагрузка на бэкенд падает в 10 раз, а пользователи забывают про F5. Экономия на инфраструктуре достигает 40% по сравнению с облачными сервисами. Свяжитесь с нами, чтобы оценить выгоду для вашего проекта.
Почему Centrifugo, а не Socket.IO или Pusher?
Centrifugo написан на Go, потребляет минимум ресурсов — один инстанс на 2 vCPU держит 20 000+ WebSocket-соединений. Socket.IO после 1 000 клиентов начинает тормозить и жрать память. Pusher/Ablity — платные, с лимитами по объёму сообщений и каналам. Centrifugo бесплатен, весь код у вас на сервере, никаких лимитов. Плюс совместимость с любым бэкендом через HTTP API или gRPC.
Как Centrifugo масштабируется под нагрузкой?
Centrifugo горизонтально масштабируется с помощью Redis Pub/Sub: добавление нового узла автоматически распределяет каналы. При отказе одного инстанса соединения перераспределяются. Мы настраиваем кластер для проектов с 50 000+ одновременных подключений, обеспечивая задержку доставки <100 мс.
Что мы делаем в рамках услуги
Архитектура интеграции
Ваш бэкенд (Laravel, Node.js, Django, Go — любой)
│── HTTP API → Centrifugo (публикация)
│── gRPC API → Centrifugo
│
Клиент (браузер) ◄── WebSocket ──► Centrifugo
Установка через Docker
# docker-compose.yml
services:
centrifugo:
image: centrifugo/centrifugo:v5
command: centrifugo -c /centrifugo/config.json
ports:
- "8000:8000"
volumes:
- ./centrifugo-config.json:/centrifugo/config.json
// centrifugo-config.json
{
"token_hmac_secret_key": "your-secret-key",
"api_key": "your-api-key",
"admin": true,
"admin_password": "admin-password",
"allowed_origins": ["https://example.com"],
"namespaces": [
{
"name": "notifications",
"subscribe_for_client": false,
"history_size": 100,
"history_ttl": "24h"
},
{
"name": "chat",
"presence": true,
"join_leave": true,
"history_size": 500,
"history_ttl": "1h"
}
]
}
Сервер: генерация JWT и публикация
import jwt from 'jsonwebtoken';
import axios from 'axios';
// Токен для клиента
function generateCentrifugoToken(userId: string): string {
return jwt.sign(
{
sub: userId,
exp: Math.floor(Date.now() / 1000) + 3600
},
process.env.CENTRIFUGO_SECRET,
{ algorithm: 'HS256' }
);
}
// Публикация сообщения через HTTP API
async function publishMessage(channel: string, data: unknown): Promise<void> {
await axios.post(`${process.env.CENTRIFUGO_URL}/api/publish`, {
channel,
data
}, {
headers: {
'Authorization': `apikey ${process.env.CENTRIFUGO_API_KEY}`,
'Content-Type': 'application/json'
}
});
}
// Отправить уведомление пользователю
await publishMessage(`notifications#${userId}`, {
type: 'order_shipped',
orderId: '12345',
message: 'Ваш заказ отправлен'
});
// Batch publish
await axios.post(`${process.env.CENTRIFUGO_URL}/api/batch`, {
commands: userIds.map(id => ({
publish: {
channel: `notifications#${id}`,
data: notification
}
}))
});
Клиент (JavaScript)
import { Centrifuge } from 'centrifuge';
const centrifuge = new Centrifuge(process.env.NEXT_PUBLIC_CENTRIFUGO_URL, {
token: await fetchCentrifugoToken(), // от вашего бэкенда
debug: process.env.NODE_ENV === 'development'
});
centrifuge.on('connect', (ctx) => {
console.log('Connected to Centrifugo', ctx.transport);
});
centrifuge.on('disconnect', (ctx) => {
console.log('Disconnected', ctx.reason);
});
// Подписка на персональные уведомления
const sub = centrifuge.newSubscription(`notifications#${userId}`, {
getToken: async (ctx) => {
// Получить subscription токен для private канала
const resp = await fetch('/api/centrifugo/subscription-token', {
method: 'POST',
body: JSON.stringify({ channel: ctx.channel })
});
return (await resp.json()).token;
}
});
sub.on('publication', (ctx) => {
const notification = ctx.data;
showNotification(notification.message);
});
sub.on('subscribing', (ctx) => console.log('Subscribing...'));
sub.on('subscribed', (ctx) => console.log('Subscribed'));
sub.subscribe();
centrifuge.connect();
Presence — онлайн-статусы
Для отслеживания онлайн-статусов используется метод presence(), который возвращает список активных клиентов. Например, для документа в совместном редактировании: centrifuge.subscription('chat:doc-123').presence() возвращает объект с клиентами. Подписка на события join и leave обновляет список в реальном времени.
Подробнее о настройке JWT
JWT-токен для подключения должен содержать sub (ID пользователя) и exp (срок действия). Для private каналов требуется дополнительный subscription-токен, который запрашивается через ваш бэкенд. Мы используем два разных секрета: один для connect-токенов, другой для subscription-токенов. Это предотвращает несанкционированный доступ к каналам.
Как обеспечить безопасность real-time каналов?
Все соединения выполняются через WSS (WebSocket over HTTPS). Для доступа к каналам требуются JWT-токены: connect-токен для подключения к серверу, subscription-токен для подписки на private каналы. Токены имеют ограниченный срок жизни (обычно 1 час) и автоматически обновляются на клиенте. Для администраторских каналов мы добавляем проверку прав на стороне бэкенда при выдаче subscription-токена.
Как мы внедряем Centrifugo: процесс работы
- Аналитика — выявляем сценарии real-time: уведомления, чат, лента, совместное редактирование. Определяем нагрузку: сколько соединений, частота публикаций, размер сообщений.
- Проектирование — выбираем namespace, настраиваем каналы (personal, room, admin), продумываем модель разрешений. Проектируем схему JWT и subscription-токенов.
- Установка и конфигурация — разворачиваем Centrifugo в Docker, настраиваем переменные окружения, SSL-сертификаты (WSS), Redis для кластеризации.
- Интеграция с бэкендом — пишем модуль публикации: отправка уведомлений, batch-публикации, генерация токенов. Тестируем N+1 запросы.
- Интеграция с фронтендом — подключаем SDK, реализуем подписки, обработку событий, переподключение при обрыве.
- Тестирование — нагрузочные тесты, проверка приватных каналов, отказоустойчивость, замеры TTFB и LCP.
- Деплой и мониторинг — настраиваем мониторинг метрик (количество соединений, задержки, ошибки), алерты, резервное копирование конфигов.
Ориентировочные сроки
| Этап | Время |
|---|---|
| Базовый (установка + одно пространство) | 3–5 дней |
| Расширенный (+ private каналы, presence) | 5–8 дней |
| Комплексный (+ кластеризация, Redis, мониторинг) | 8–12 дней |
Стоимость варьируется в зависимости от сложности — мы рассчитываем её индивидуально. Гарантируем фиксацию сроков и цены.
Типичные ошибки и как мы их избегаем
- Слишком длинные JWT — ставим срок жизни 1 час, автоматическое обновление на клиенте.
- Отсутствие backpressure — настраиваем лимиты публикаций и конфигурируем TTL истории.
- Путаница с токенами — чётко разделяем connect token и subscription token, используем разные секреты.
- Забыли про CORS — разрешаем только ваши домены в
allowed_origins.
Сравнение: Centrifugo vs облачные сервисы
| Критерий | Centrifugo | Pusher / Ablity | Socket.IO |
|---|---|---|---|
| Цена | Бесплатно (только ресурсы) | От $50/мес за 100 коннектов | Бесплатно |
| Максимум соединений на 1 сервер | 20 000+ | Ограничено планом | ~1 500 |
| Контроль над данными | Полный | Нет (на серверах вендора) | Полный |
| Кастомные протоколы | gRPC + HTTP API | Только HTTP | WebSocket + HTTP long-polling |
| Поддержка history | Встроенная | В Pusher — только cache | Нет |
Свяжитесь с нами для консультации — мы оценим ваш проект за 1 день. Закажите интеграцию Centrifugo прямо сейчас и получите стабильное real-time за неделю.







