Реализация Real-Time трекинга курьера/заказа на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

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

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

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Реализация Real-Time трекинга курьера/заказа на сайте
Средний
~5 дней
Часто задаваемые вопросы

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

Этапы разработки

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

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

Реализация Real-Time трекинга курьера/заказа на сайте

Пользователь оформил заказ и ждёт курьера. Страница «Мои заказы» с полем «статус: в пути» — это уже прошлый век. Современный стандарт — карта с живым маркером курьера и счётчиком «прибудет через N минут». Технически это связка трёх компонентов: мобильное приложение/устройство курьера, бэкенд-приложение, клиентский браузер. Мы построили такие системы для 30+ проектов доставки — от локальных сервисов до федеральных сетей. Свяжитесь с нами для расчёта вашего проекта.

Почему polling базы — плохой выбор?

Казалось бы, можно опрашивать сервер каждые 5 секунд: GET /api/order/status. Но это создаёт лишнюю нагрузку на сервер, увеличивает TTFB для остальных запросов и не даёт мгновенного обновления. WebSocket в разы эффективнее: событие приходит сразу, без задержки опроса. Однако простая реализация WebSocket без авторизации открывает доступ к чужим заказам. Мы используем PrivateChannel Laravel Broadcasting с проверкой прав.

Архитектура потока данных

[Устройство курьера]
    GPS → POST /api/courier/location каждые 3–5с
        ↓
[Backend]
    Сохранить в Redis (TTL 60s)
    Publish в Redis Pub/Sub канал order:{id}
        ↓
[WebSocket сервер (Laravel Reverb / Pusher)]
    Broadcast event LocationUpdated
        ↓
[Браузер клиента]
    Обновить маркер на карте

Геопозиции не хранятся в PostgreSQL при каждом обновлении — это 720 записей в час на одного курьера. В базу пишем только при смене статуса заказа и финальную позицию при завершении. Текущая позиция — в Redis с TTL.

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

Redis обеспечивает низкую задержку записи и чтения (субмиллисекунды) и автоматическое удаление устаревших данных через TTL. Для трекинга это критично: позиция курьера актуальна лишь короткое время. Постоянная запись в PostgreSQL привела бы к 720 записям в час на одного курьера — неоправданная нагрузка на базу. С Redis мы экономим ресурсы и ускоряем чтение: получить текущую позицию можно за один запрос к in-memory хранилищу. Экономия на серверной инфраструктуре может достигать 40% за счёт отказа от постоянного polling.

Пример конфигурации WebSocket-сервера с Laravel Reverb Для продакшена рекомендуется использовать Reverb с горизонтальным масштабированием через Redis. Настройка сводится к установке пакета, публикации конфига и запуску воркера. Подробнее — в официальной документации Laravel.

Таблица заказов

CREATE TABLE delivery_orders (
    id             BIGSERIAL PRIMARY KEY,
    user_id        BIGINT NOT NULL REFERENCES users(id),
    courier_id     BIGINT REFERENCES couriers(id),
    status         VARCHAR(50) NOT NULL DEFAULT 'pending',
                   -- pending | assigned | picked_up | in_transit | delivered | failed
    address_lat    DECIMAL(10, 8),
    address_lng    DECIMAL(11, 8),
    address_text   VARCHAR(500),
    estimated_at   TIMESTAMP,
    delivered_at   TIMESTAMP,
    created_at     TIMESTAMP NOT NULL DEFAULT NOW()
);

CREATE TABLE delivery_status_log (
    id         BIGSERIAL PRIMARY KEY,
    order_id   BIGINT NOT NULL REFERENCES delivery_orders(id),
    status     VARCHAR(50) NOT NULL,
    lat        DECIMAL(10, 8),
    lng        DECIMAL(11, 8),
    note       TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

API курьера: обновление позиции и broadcast

class CourierLocationController extends Controller
{
    public function update(Request $request, DeliveryOrder $order): JsonResponse
    {
        $data = $request->validate([
            'lat' => 'required|numeric|between:-90,90',
            'lng' => 'required|numeric|between:-180,180',
        ]);

        $key = "courier_location:{$order->courier_id}";
        Redis::setex($key, 60, json_encode([
            'lat'      => $data['lat'],
            'lng'      => $data['lng'],
            'order_id' => $order->id,
            'ts'       => now()->timestamp,
        ]));

        broadcast(new CourierLocationUpdated(
            orderId: $order->id,
            lat:     $data['lat'],
            lng:     $data['lng'],
            eta:     $this->calculateEta($order, $data['lat'], $data['lng']),
        ));

        return response()->json(['ok' => true]);
    }

    private function calculateEta(DeliveryOrder $order, float $lat, float $lng): ?int
    {
        $distanceKm = $this->haversineKm($lat, $lng, $order->address_lat, $order->address_lng);
        return (int) round($distanceKm / 30 * 60);
    }
}

// CourierLocationUpdated event
class CourierLocationUpdated implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public readonly int    $orderId,
        public readonly float  $lat,
        public readonly float  $lng,
        public readonly ?int   $eta,
    ) {}

    public function broadcastOn(): Channel
    {
        return new PrivateChannel("order.{$this->orderId}");
    }

    public function broadcastWith(): array
    {
        return [
            'lat' => $this->lat,
            'lng' => $this->lng,
            'eta' => $this->eta,
        ];
    }
}

Авторизация канала в routes/channels.php:

Broadcast::channel('order.{orderId}', function (User $user, int $orderId) {
    return $user->id === DeliveryOrder::find($orderId)?->user_id;
});

Канал PrivateChannel гарантирует, что только владелец заказа получает координаты. Это обязательное требование безопасности.

Клиентская часть: карта и анимация маркера

Используем Mapbox для отображения карты. Альтернатива — Яндекс.Карты, предпочтительнее для СНГ.

import mapboxgl from 'mapbox-gl';
// ... инициализация карты
Echo.private(`order.${orderId}`)
    .listen('CourierLocationUpdated', ({ lat, lng, eta }) => {
        animateMarker(courierMarker, courierMarker.getLngLat().toArray(), [lng, lat]);
        if (eta !== null) {
            document.getElementById('eta').textContent =
                eta < 2 ? 'Курьер уже рядом' : `Прибудет через ~${eta} мин`;
        }
    });

function animateMarker(marker, from, to, duration = 500) {
    const start = performance.now();
    function step(now) {
        const t = Math.min((now - start) / duration, 1);
        const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
        const lng = from[0] + (to[0] - from[0]) * ease;
        const lat = from[1] + (to[1] - from[1]) * ease;
        marker.setLngLat([lng, lat]);
        if (t < 1) requestAnimationFrame(step);
    }
    requestAnimationFrame(step);
}

Сравнение методов расчёта ETA

Метод Точность Время построения маршрута Зависимости
По прямой (Haversine) Низкая (игнорирует дороги) Мгновенно Нет
Routing API (OSRM) Высокая (учитывает дороги) 50–200 мс Внешний сервис
Google Directions API Очень высокая (трафик) 200–500 мс API-ключ

Сравнение подходов к real-time доставке

Подход Задержка обновлений Нагрузка на сервер Сложность реализации
Polling (HTTP каждые 5 с) 5 сек в среднем Высокая Низкая
WebSocket (постоянное соединение) Мгновенно Низкая Средняя
Server-Sent Events Мгновенно Низкая Средняя

Как настроить Private Channel за 5 шагов

  1. Установите Laravel Reverb или Pusher.
  2. Настройте broadcasting в config/broadcasting.php.
  3. Создайте Event, реализующий ShouldBroadcast.
  4. Определите PrivateChannel в routes/channels.php.
  5. Подпишитесь на канал на клиенте через Echo.

Что входит в результат

  • Аналитика: интеграция с мобильным приложением курьера, согласование протокола.
  • Проектирование: схема данных, WebSocket-архитектура, авторизация.
  • Реализация: бэкенд на Laravel с Redis, broadcast, карта на фронтенде.
  • Тестирование: нагрузочное тестирование WebSocket (1k+ одновременных подключений).
  • Документация: описание API, инструкция по деплою.
  • Поддержка: гарантия на код, обучение команды.

Сроки ориентировочно

  • Базовый трекинг (Redis + broadcast + карта): от 4 до 5 дней.
  • Авторизация Private Channel + логика доступа: от 1 дня.
  • ETA-расчёт по прямой (Haversine): от 0.5 дня.
  • ETA через Routing API: от 1 до 2 дней.
  • Анимация маркера + сглаживание пути: от 1 дня.
  • Push-уведомления при смене статуса: от 1 до 2 дней.
  • Административная панель диспетчера: от 3 до 4 дней.

Почему стоит заказать у нас

Мы реализовали real-time трекинг для 30+ проектов, включая крупные службы доставки. Гарантируем стабильную работу WebSocket-соединений при тысячах курьеров, соблюдение приватности каналов и плавную анимацию без рывков. Закажите оценку вашего проекта — предложим оптимальное решение.

Разработка систем реального времени: 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)

Как правильно выбрать транспорт: пошаговая инструкция

  1. Определите сценарий обмена данными: однонаправленный (сервер → клиент) — SSE; двунаправленный с низкой задержкой — WebSocket; аудио/видео — WebRTC.
  2. Оцените требования к задержке. Если приемлемо <500 мс — подойдёт SSE; для <100 мс и двунаправленности — WebSocket; для <50 мс и P2P — WebRTC.
  3. Проверьте бюджет на инфраструктуру. SSE использует обычные HTTP‑серверы, WebSocket требует держать соединения в памяти, WebRTC может потребовать TURN‑сервер (от 3000 ₽/мес за 1 ТБ трафика).
  4. Учтите масштабирование: для 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).

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