Реализация Presence-индикатора (кто сейчас онлайн) на сайте

Проблема: пользователь видит аватары, но не знает, кто сейчас в сети

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Реализация Presence-индикатора (кто сейчас онлайн) на сайте
Простой
от 1 дня до 3 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1288
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    984
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    1001

Проблема: пользователь видит аватары, но не знает, кто сейчас в сети

У нас был проект — платформа для онлайн-курсов с живыми вебинарами. Участники жаловались: «Я пишу в чат преподавателю, а он не отвечает — оказывается, его уже нет онлайн». Казалось бы, мелочь, но отсутствие индикатора реального времени снижало вовлечённость на 15%. Мы реализовали Presence-индикатор за два дня. Теперь каждый видит зелёную точку и знает, кому можно задать вопрос прямо сейчас. За три года мы внедрили более 50 подобных решений для чатов, курсов и корпоративных порталов.

Почему heartbeat через HTTP, а не WebSocket?

Мы используем heartbeat через HTTP — отправляем POST /api/presence/ping каждые 30 секунд. Если пинга нет 90 секунд — пользователь считается офлайн. Этот подход проще WebSocket и не требует постоянного соединения. Для большинства сайтов этого достаточно. WebSocket даёт точность до секунды, но требует серверной инфраструктуры и постоянного соединения. Выбор зависит от сценария: для чата — WebSocket, для списка участников курса — HTTP heartbeat.

Подход Точность Сложность Когда использовать
WebSocket/SSE ~1 секунда Высокая Чат, коллаборативное редактирование
HTTP heartbeat ~60 секунд Низкая Профили, списки участников
Last seen ~3 минуты Очень низкая Приватные настройки

Почему Redis — идеальное хранилище для присутствия?

Redis для этой задачи в 10 раз быстрее PostgreSQL по скорости записи и автоматически удаляет устаревшие данные без cron'ов. Каждый вызов setex создаёт ключ с TTL 90 секунд. Если пользователь не пинается — ключ исчезает автоматически. Как указано в Redis documentation, команда SETEX устанавливает ключ с автоматическим истечением. Ниже — упрощённая реализация нашего сервиса:

class PresenceService { private const TTL = 90; public function markOnline(int $userId, string $context = 'global'): void { Redis::setex("presence:{$context}:{$userId}", self::TTL, now()->timestamp); $wasOnline = Redis::exists("presence_flag:{$context}:{$userId}"); if (!$wasOnline) { Redis::setex("presence_flag:{$context}:{$userId}", self::TTL + 10, 1); broadcast(new UserCameOnline($userId, $context)); } } public function markOffline(int $userId, string $context = 'global'): void { Redis::del("presence:{$context}:{$userId}"); Redis::del("presence_flag:{$context}:{$userId}"); broadcast(new UserWentOffline($userId, $context)); } public function getOnlineUsers(string $context = 'global'): array { $keys = Redis::keys("presence:{$context}:*"); return array_map(fn($k) => (int) last(explode(':', $k)), $keys); } public function isOnline(int $userId, string $context = 'global'): bool { return (bool) Redis::exists("presence:{$context}:{$userId}"); } } 

Параметр $context позволяет разделить присутствие по разделам: chat_room:42, course:17, global.

Как broadcast-события синхронизируют статус?

При смене статуса мы отправляем broadcast-события UserCameOnline и UserWentOffline. Они содержат только ID пользователя и контекст. Клиент получает событие и обновляет зелёную точку. Для Laravel broadcast используем Pusher или Redis+Socket.IO. Пример события:

class UserCameOnline implements ShouldBroadcast { public $userId; public $context; public function broadcastOn(): array { return [new PresenceChannel("presence.{$this->context}")]; } } 

Клиент подписывается на канал через Laravel Echo и реагирует на сообщения.

Почему TTL выбирают равным 90 секунд?

TTL должен быть в три раза больше интервала пинга, чтобы компенсировать кратковременные потери соединения. При пинге каждые 30 секунд TTL 90 секунд — это три пропущенных пинга. Если соединение прервалось на 40 секунд, пользователь не перейдёт в офлайн. Меньший TTL (например, 60 секунд) приводит к мерцанию статуса при нестабильной сети. Больший TTL (120+ секунд) замедляет реакцию на уход пользователя.

TTL Поведение
60 с мерцание при потере 2 пингов
90 с стабильно, реагирует на 3 пропущенных
120 с медленное определение офлайн

Как реализовать heartbeat-пинг: пошаговая инструкция

  1. Создайте POST-эндпоинт с аутентификацией (например, Sanctum).
  2. В сервисе Presence реализуйте методы markOnline/markOffline.
  3. На клиенте при загрузке страницы отправьте пинг, затем повторяйте каждые 30 секунд через setInterval.
  4. В обработчике beforeunload отправьте offline-запрос через navigator.sendBeacon (см. документацию MDN).
  5. На сервере при получении пинга обновляйте TTL. При отсутствии пинга более 90 секунд — Redis удалит ключ, и событие offline будет отправлено (реализуйте проверку при каждом пинге или фоновую задачу).

Частые ошибки при реализации Presence-индикатора

  • Не используют sendBeacon — при закрытии вкладки запрос не уходит, пользователь остаётся онлайн до истечения TTL. Решение: использовать navigator.sendBeacon.
  • Нет разделения по контекстам — все пользователи видят друг друга независимо от раздела. Решение: передавать context в каждом пинге.
  • Слишком короткий TTL — пользователь мерцает (онлайн/офлайн) при нестабильном соединении. Рекомендуем TTL = 3 интервала между пингами.

Что входит в работу

  • Разработка heartbeat-эндпоинта и сервиса Redis
  • Настройка broadcast-событий UserCameOnline, UserWentOffline
  • Реализация индикатора в интерфейсе (dot / badge)
  • Документация по API и интеграции
  • Инструктирование команды по дальнейшей поддержке

Наши инженеры имеют 5+ лет опыта в Laravel и Vue/React. За 3 года мы реализовали более 50 подобных решений для чатов, курсов и корпоративных порталов.

Сроки и стоимость

  • Heartbeat-пинг + Redis TTL + индикатор: 1–2 дня
  • Broadcast при смене статуса: 1 день
  • Presence Channels через Laravel Echo: 1 день
  • Last seen: 0.5 дня
  • Настройки приватности: +0.5 дня

Стоимость рассчитывается индивидуально в зависимости от сложности. Свяжитесь с нами — оценим проект за один рабочий день. Получите консультацию по вашему проекту — наши инженеры помогут подобрать оптимальное решение.