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

Пользователи часто не знают, кто из коллег или клиентов сейчас на сайте, и это замедляет общение и снижает вовлечённость. Мы реализуем Presence-индикатор, который в реальном времени показывает онлайн-статус участников. Наша команда берёт на себя весь цикл — от аудита до внедрения и поддержки, обеспечивая надёжную работу и масштабирование решения вместе с вашим бизнесом.

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

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

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

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

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

У нас был проект — платформа для онлайн-курсов с живыми вебинарами. Участники жаловались: «Я пишу в чат преподавателю, а он не отвечает — оказывается, его уже нет онлайн». Казалось бы, мелочь, но отсутствие индикатора реального времени снижало вовлечённость на 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 дня

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

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

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

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060