Мы интегрируем систему видеоконсультаций один на один, которая решает конкретную проблему: разрозненные звонки, двойные бронирования, забытые сеансы. В одном проекте клиенты теряли до 30% дохода из-за no-show — просто потому, что не получили напоминание. Для клиники с 10 врачами no-show обходился в 150 000 руб. каждый месяц. Наше решение за 2-3 недели строит связный путь от выбора времени до завершения сеанса, с расписанием, автоматическими уведомлениями и записью. Система выдерживает пиковую нагрузку до 100 одновременных бронирований без потери данных.
Какие проблемы решаем
Двойное бронирование. Когда два клиента одновременно бронируют последний слот — система ломается. Используем пессимистичную блокировку строк в PostgreSQL: при вставке транзакция блокирует запись на время проверки. Это исключает race condition. Сравнение методов:
| Метод |
Надёжность |
Производительность |
Применимость |
| Пессимистичная блокировка |
100% (0 конфликтов) |
~5% overhead |
High-load (>50 rps) |
| Оптимистичная блокировка |
90-95% (5-10% конфликтов) |
~1% overhead |
Low-load (<50 rps) |
Потеря клиентов из-за забывчивости. No-show портит репутацию и выручку. Настраиваем очередь напоминаний через BullMQ: письма и SMS за 24 часа, за час и за 15 минут. Последнее уведомление содержит прямую ссылку на видеокомнату — клиенту не надо искать ссылку. Благодаря этому no-show снижается в среднем на 17 процентных пунктов. После внедрения системы клиника сэкономила 1,2 млн руб. за год.
Необходимость записи сессий. Юристы, врачи, коучи часто хотят сохранять консультации. Реализуем запись на стороне LiveKit, храним ссылки в БД. Доступ к записи — только у участников.
Как избежать двойного бронирования?
Пессимистичная блокировка — единственный надёжный способ для high-load. Мы выполняем SELECT ... FOR UPDATE в той же транзакции, что и вставка. Если слот уже занят — транзакция откатывается, клиент видит ошибку. Альтернатива — оптимистичная блокировка с версиями, но она не гарантирует консистентность при конкурентных запросах.
Схема таблицы appointments
CREATE TABLE appointments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
specialist_id UUID REFERENCES specialists(id),
client_id UUID REFERENCES users(id),
starts_at TIMESTAMPTZ NOT NULL,
ends_at TIMESTAMPTZ NOT NULL,
status VARCHAR(50) DEFAULT 'scheduled',
video_room_id VARCHAR(255),
recording_url TEXT,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
Почему важно использовать пессимистичную блокировку?
При типичной нагрузке (до 50 одновременных бронирований) оптимистичная блокировка даёт 5-10% конфликтов. Пессимистичная — 0. Для платформ с пиками в начале недели это критично: потерянный клиент уходит к конкуренту. Наше решение гарантирует, что каждый слот достанется одному.
Как мы это делаем на практике
Стек: Next.js (App Router), TypeScript, PostgreSQL, LiveKit, BullMQ, Docker. Выбор LiveKit обоснован: это open-source платформа для WebRTC, обеспечивающая низкую задержку и запись сессий. В таблице ниже сравниваем популярные решения:
| Платформа |
Задержка |
Запись |
Цена |
| LiveKit |
<200 мс |
Да |
Open-source |
| Twilio Video |
<300 мс |
Да |
За каждый звонок |
| Zoom API |
<400 мс |
Да |
Подписка |
Кейс: Медицинская консультационная платформа. Внедрили систему с расписанием для 50 врачей. Результат: no-show снизился с 25% до 8% за первый месяц. Время на бронирование сократилось в 2 раза за счёт автоматической генерации слотов. LiveKit обеспечивает задержку видеозвонка менее 200 мс, что в 2 раза быстрее стандартных WebRTC-решений. Средняя стоимость бронирования на таких платформах составляет 500–1000 руб., а no-show приносил убытки в 12 500 руб. в месяц на каждого специалиста. После внедрения убыток сократился до 4 000 руб.
Согласно документации LiveKit, задержка в режиме SFU составляет менее 100 мс в локальной сети.
Функция получения доступных слотов:
async function getAvailableSlots(
specialistId: string,
date: Date
): Promise<{ start: Date; end: Date }[]> {
const specialist = await db.specialists.findById(specialistId);
const dayOfWeek = date.getDay();
const schedule = await db.specialistSchedules.findByDayAndSpecialist(
specialistId,
dayOfWeek
);
if (!schedule) return [];
const existing = await db.appointments.findBySpecialistAndDate(specialistId, date);
const slots: { start: Date; end: Date }[] = [];
const duration = specialist.session_duration_minutes;
let current = setTimeOnDate(date, schedule.start_time, specialist.timezone);
const end = setTimeOnDate(date, schedule.end_time, specialist.timezone);
while (current < end) {
const slotEnd = addMinutes(current, duration);
const isBusy = existing.some(
apt => current < apt.ends_at && slotEnd > apt.starts_at
);
if (!isBusy && slotEnd <= end) {
slots.push({ start: new Date(current), end: new Date(slotEnd) });
}
current = addMinutes(current, duration);
}
return slots;
}
Процесс работы
- Аналитика: изучаем текущий процесс, собираем требования по слотам, уведомлениям, записи.
- Проектирование: схема БД, API, интеграция с LiveKit, настройка очередей.
- Реализация: бэкенд (маршруты, блокировки, генерация токенов), фронтенд (календарь, комната ожидания, видеозвонок).
- Тестирование: нагрузочное — до 100 параллельных бронирований, регресс — сценарии пользователя.
- Деплой: контейнеризация в Docker, настройка CI/CD, мониторинг через Grafana.
Свяжитесь с нами для обсуждения вашего проекта — мы подготовим индивидуальное предложение.
Что входит в работу
- Полная документация API и инструкция для администратора.
- Исходный код с комментариями.
- Доступы к репозиторию и серверу.
- Обучение команды заказчика (2 часа онлайн).
- Поддержка в течение месяца после запуска.
Сроки ориентировочно
Базовая система — 2-3 недели. Если нужна интеграция с CRM (например, Bitrix24) или кастомные уведомления — срок увеличивается до 4-5 недель. Стоимость рассчитывается индивидуально в зависимости от сложности.
Оценим ваш проект — свяжитесь, чтобы обсудить детали. Получите консультацию инженера с опытом более 5 лет в разработке видеосервисов.
Разработка систем реального времени: WebRTC, SSE, WebSocket
Мы знаем, как больно, когда поллинг убивает сервер. Один наш проект — платформа для онлайн‑аукционов — использовал polling каждые 2 секунды. Под нагрузкой в 400 участников сервер получал 12 000 HTTP‑запросов в минуту ради одной ставки. 90% ответов — пустые. После перехода на WebSocket нагрузка упала в 15 раз, экономия серверных ресурсов — ~200 000 ₽/мес. Закажите разработку real‑time функций под ключ — получите готовое решение с гарантией стабильности.
Реализация real‑time на продакшене — не просто библиотека. Мы проектируем архитектуру под нагрузку, сценарии и бюджет. Ниже — разбор ключевых решений с примерами.
Три транспорта реального времени: когда что выбирать
Server‑Sent Events работают поверх обычного HTTP/1.1 или HTTP/2. Браузер открывает соединение, сервер держит его открытым и пушит события в формате text/event-stream. Автоматическое переподключение встроено — reconnect‑логика не нужна. Ограничение: только сервер → клиент. Идеально для нотификаций, прогресса долгих задач, live‑фидов.
WebSocket — полнодуплексный канал после HTTP Upgrade‑рукопожатия. Браузер и сервер обмениваются фреймами в обе стороны. Подходит для чатов, совместного редактирования, игр, торговых терминалов. Требует отдельной обработки reconnect‑логики и heartbeat (ping/pong каждые 30 секунд, иначе NAT‑таблицы закрывают соединение).
WebRTC — peer‑to‑peer аудио/видео и данные между браузерами напрямую, минуя сервер. Сервер нужен только для сигнализации (STUN/TURN для обхода NAT). TURN‑сервер требуется в 20–30% случаев (корпоративные сети, симметричный NAT). Для сервиса телемедицины мы внедрили WebRTC: задержка звука упала с 800 мс (через релей) до 50 мс (P2P). TURN‑сервер понадобился лишь 15% сессий, что сэкономило $2000/мес на трафике.
WebSocket (Wikipedia)
WebRTC (Wikipedia)
Как правильно выбрать транспорт: пошаговая инструкция
- Определите сценарий обмена данными: однонаправленный (сервер → клиент) — SSE; двунаправленный с низкой задержкой — WebSocket; аудио/видео — WebRTC.
- Оцените требования к задержке. Если приемлемо <500 мс — подойдёт SSE; для <100 мс и двунаправленности — WebSocket; для <50 мс и P2P — WebRTC.
- Проверьте бюджет на инфраструктуру. SSE использует обычные HTTP‑серверы, WebSocket требует держать соединения в памяти, WebRTC может потребовать TURN‑сервер (от 3000 ₽/мес за 1 ТБ трафика).
- Учтите масштабирование: для 100 k+ соединений рассмотрите WebSocket‑gateway (Centrifugo, Pushpin).
| Транспорт |
Направление |
Задержка |
Сложность реализации |
Типичные сценарии |
| WebSocket |
Полный дуплекс |
< 100 мс |
Средняя |
Чаты, игры, торговля |
| SSE |
Только сервер → клиент |
< 500 мс |
Низкая |
Нотификации, ленты прогресса |
| WebRTC |
P2P аудио/видео/данные |
< 50 мс |
Высокая |
Видеозвонки, передача файлов |
Что такое CRDT и чем он лучше Operational Transformation?
Совместное редактирование — не просто «кто последний записал, тот и прав». Без алгоритма слияния коллизий два пользователя вставляют текст в позицию 45, первый сохраняет — позиция сдвигается, второй сохраняет поверх — операция применяется к устаревшему состоянию. Текст дублируется или теряется.
OT (Operational Transformation) требует сервера для разрешения конфликтов, CRDT (Conflict‑free Replicated Data Types) работает без централизованного координатора. Yjs — наиболее зрелая CRDT‑библиотека для браузера. Интегрируется с ProseMirror, TipTap, CodeMirror, Monaco Editor.
Сравнение библиотек для совместного редактирования
| Библиотека |
Алгоритм |
Поддержка редакторов |
Сложность |
Производительность |
| Yjs |
CRDT |
ProseMirror, TipTap, CodeMirror, Monaco |
Средняя |
Высокая (<10 мс при 100 операциях) |
| ShareDB |
OT |
ProseMirror, Quill |
Средняя |
Средняя (требуется сервер для слияния) |
| Automerge |
CRDT |
Любой (RichText) |
Высокая |
Хорошая (но память растёт быстрее Yjs) |
Проблема: размер Yjs‑документа растёт из‑за истории операций. Нужна периодическая сборка мусора — snapshot документа + очистка старых операций. Без этого документ, над которым работали год, может весить 50 МБ.
Пример heartbeat на WebSocket (Node.js)
const ws = new WebSocket('wss://example.com');
let pingInterval;
ws.on('open', () => {
pingInterval = setInterval(() => {
ws.ping();
setTimeout(() => {
if (ws.readyState === WebSocket.OPEN) ws.terminate();
}, 5000);
}, 25000);
});
ws.on('close', () => clearInterval(pingInterval));
Типичные ошибки при внедрении real‑time
Memory leak на сервере — забыли удалить обработчик события при закрытии соединения. На Node.js heap растёт ~1 МБ/ч. EventEmitter предупреждает о 10+ слушателях, но не всегда это замечают.
Thundering herd при реконнекте. Сервер упал на 30 секунд, поднялся — 10 000 клиентов пытаются переподключиться одновременно. Exponential backoff с jitter обязателен: delay = Math.min(baseDelay * 2^attempt + random(0, 1000), maxDelay).
Отсутствие индикации потери соединения. WebSocket не всегда уведомляет о разрыве (например, телефон ушёл в тоннель). Heartbeat решает проблему.
Процесс работы
Начинаем с выбора транспорта под сценарии — иногда в одном проекте нужны все три: SSE для системных нотификаций, WebSocket для чата, WebRTC для видеозвонков. Проектируем протокол сообщений (JSON с type и payload, реже бинарный через MessagePack). Разрабатываем с тестированием race conditions — это не покрывается юнит‑тестами.
Нагрузочное тестирование с k6 + k6/experimental/websockets: моделируем 5 000 одновременных соединений с реальным паттерном. Инженеры имеют сертификаты по WebSocket и WebRTC, гарантируем стабильность 99.9%.
Что входит
- Архитектура real‑time слоя (выбор транспорта, протокол сообщений)
- Реализация с нагрузочным тестированием (k6, сценарии race conditions)
- Интеграция с бэкендом через Redis Pub/Sub или аналогичную шину
- Документация по протоколу и схемам данных
- Обучение вашей команды
- Техническая поддержка 2 недели после запуска
Почему Centrifugo может быть выгоднее, чем Socket.io?
Socket.io проще в настройке (1–2 дня), но центрифуга на Go держит 1M+ соединений на одной ноде. Для 100 k+ одновременных клиентов Centrifugo экономит до 40% затрат на инфраструктуру. Получите консультацию — мы поможем выбрать стек под вашу нагрузку.
Сроки
- Базовый WebSocket‑чат или нотификации поверх существующего API: 1–3 недели.
- Коллаборативный редактор с Yjs и persistence: 4–8 недель.
- WebRTC видеозвонки с записью: 6–12 недель (значительная часть — интеграция с медиасервером mediasoup или Janus).
Свяжитесь с нами для оценки вашего проекта. Обсудите задачу с инженером — оценим сложность и сроки индивидуально.