Веб-сервис может получать позицию пользователя и показывать другим людям изменения без перезагрузки страницы, но с границами. Браузер отдаёт координаты только странице по HTTPS и только с разрешения человека, а при погашенном экране на телефоне делает это ненадёжно. Обновления доставляют опросом, потоком событий от сервера (SSE) или через WebSocket, и способ выбирают по направлению и частоте данных, а не по моде.

Отсюда порядок решений. Сначала выясняем, кто и зачем передаёт позицию, потом выбираем способ доставки, храним состояние на сервере и считаем событие подсказкой. Как это выглядит в готовом продукте, описано на странице «Real-time сервис с геолокацией», здесь разбираем сами решения.

Коротко. Позиция из браузера — заявление клиента: она приблизительна, зависит от разрешения и пропадает в фоне. Событие в реальном времени — подсказка «перечитай состояние», а само состояние лежит на сервере. Способ доставки выбирают по направлению данных и частоте: опрос, SSE или WebSocket. Маршруты хранят не дольше, чем нужно для разбора спорных случаев.

Что умеет браузер и чего не умеет

Позицию браузер отдаёт через стандартный программный интерфейс геолокации. Есть четыре условия, которые удобно знать до проектирования.

  • Только по HTTPS и только с согласия. Страница без защищённого соединения позицию не получит. Согласие запрашивает сам браузер, и в некоторых браузерах оно действует только на текущий сеанс.
  • Только пока страница живёт. Пока вкладка открыта и экран включён, позиция приходит. Когда экран гаснет или вкладка уходит в фон, обновления на телефоне могут остановиться, и это зависит от устройства и версии системы.
  • Точность разная. Телефон с приёмником спутниковой навигации даёт десятки метров или лучше, компьютер определяет место по сетям Wi-Fi и может ошибиться на много сотен метров. В помещении, во дворах и в тоннеле картина хуже.
  • Вместе с позицией приходит радиус погрешности. Его нужно сохранять и использовать, а не выбрасывать: точка с радиусом в километр и точка с радиусом в пять метров — разные данные.

Из второго пункта следует главное ограничение. Задача «непрерывно следить за курьером, пока телефон лежит в кармане с погашенным экраном» для веб-приложения не решается надёжно. Есть три выхода: исполнитель держит сервис открытым (телефон в держателе, экран включён, зарядка подключена), позиция приходит из другого источника, например из трекера в машине по его программному интерфейсу, или нужно нативное приложение. Нативные приложения мы не делаем, и об этом говорим до договора. Общий разбор выбора — в статье «PWA или нативное приложение».

Разрешение на позицию: состояния и что показывать

Запрос разрешения — самое частое место, где сервис теряет пользователя. Если запрашивать позицию сразу при открытии страницы, без объяснения, часть людей нажмёт «запретить», и браузер запомнит отказ. Поэтому сначала объясняем, зачем нужна позиция, и только потом вызываем системный запрос. Интерфейс держит пять состояний, и у каждого свой экран.

СостояниеЧто произошлоЧто показываемЧего не делаем
Не спрашивалиПользователь только пришёлКороткое объяснение и кнопка «Определить моё местоположение»; рядом ввод адресаНе вызываем системный запрос при загрузке страницы
РазрешеноПозиция приходитТочка на карте и радиус погрешности; кнопка остановить передачуНе передаём позицию дальше, чем нужно сценарию
ОтказаноПользователь нажал «запретить»Выбор точки на карте или ввод адреса и подсказка, как включить разрешение в настройкахНе просим снова при каждом открытии и не блокируем сервис
НедоступнаОшибка или истёк срок ожидания, нет сигналаПоследнюю известную точку с временем и предложение повторитьНе рисуем старую точку как текущую
ГрубаяРадиус погрешности слишком большой для задачиПросьбу уточнить: выйти из здания, включить точную геолокацию, указать адресНе принимаем решение по такой точке молча

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

Точность: чему верить в координатах

Координаты с клиента — это его заявление. Для карты в приложении этого достаточно, для решений (засчитать выезд, открыть доступ, назначить заказ) нужна проверка на сервере. Типовые странности и что с ними делать:

СимптомПричинаЧто делаем
Точка прыгает на сотни метров и возвращаетсяУстройство переключается между источниками: спутники, сети Wi-Fi, вышкиОтбрасываем точки с большим радиусом погрешности, сглаживаем маршрут на сервере, не сглаживаем «в ноль»: скорость и остановки должны остаться видны
Точка застылаЭкран погас, вкладка в фоне или пропала связьХраним время последней точки и показываем возраст данных; по истечении срока помечаем «нет данных»
Первая точка далеко от настоящейПервое определение быстрое и грубое, точная приходит позжеЖдём точку с приемлемым радиусом или показываем грубую с пометкой
Позиция «в океане» или в другом городеПодмена на устройстве: эмулятор, расширение, специальное приложениеПроверяем правдоподобие: скорость между точками, согласие с адресом и сетью; для важных решений требуем второй признак, например фото на месте
Расстояние посчитано неверноРасчёт по плоской формуле или перепутан порядок широты и долготыСчитаем расстояние по сфере или средствами пространственной базы и покрываем тестами с известными парами точек

Если задача — отметка присутствия на объекте, допуск погрешности записываем в требования вместе с тем, что делать при его превышении. Спорные отметки уходят на проверку человеку.

Какой способ доставки обновлений выбрать

Способов три, и каждый хорош для своей задачи. Выбирают по трём вопросам: в какую сторону идут данные, как часто они меняются и сколько людей слушают одно и то же.

ЗадачаНаправлениеПодходящий способПочему
Статус заказа раз в минутусервер клиенту, редкоОпрос по расписаниюПроще всего и достаточно; лишние запросы не страшны, пока страниц немного
Позиция курьера на карте клиентасервер клиенту, частоПоток событий (SSE) или WebSocketКлиент только слушает, поток нужен постоянный; SSE проще, браузер переподключается сам
Передача позиции с телефона исполнителяклиент серверу, частоОбычные запросы или WebSocketОдно направление; при редкой отправке хватает запросов, при частой удобнее одно соединение
Экран диспетчера с картой и командамив обе стороныWebSocketДиспетчер и слушает, и отправляет команды, нужна задержка меньше секунды
Курьер подъезжает, пользователь закрыл страницусервер клиенту, разовоPush-уведомлениеПриходит, когда сервис закрыт; зависит от платформы и разрешения, поэтому критичное дублируем

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

Событие и состояние: как не потерять изменения при обрыве

Главная ошибка real-time — считать, что сообщение дойдёт. Сеть на телефоне пропадает, вкладка засыпает, балансировщик обрывает простаивающее соединение. Поэтому событие не несёт «истину», оно говорит клиенту: «заказ изменился, перечитай». Как это строится:

  1. Состояние хранится на сервере. У заказа, очереди или исполнителя есть текущая запись в базе, которую можно получить одним запросом.
  2. У записи есть версия. Событие приходит с номером версии. Если клиент получил версию 7, а потом пришла 6 из-за гонки, она игнорируется.
  3. При подключении клиент сначала читает состояние, потом подписывается. Так он не пропустит изменение между запросом и подпиской.
  4. После обрыва клиент подключается заново и снова читает состояние. У SSE есть идентификатор последнего события, и сервер может дослать пропущенное; у WebSocket переподключение и запрос пропущенного пишем сами.
  5. Сервер проверяет, что соединение живо. Периодический служебный сигнал позволяет заметить «мёртвое» соединение, иначе оно висит и занимает память.

Повторная доставка не должна ничего ломать. Событие «заказ выдан» дважды не создаёт два документа, а команда «назначить исполнителя» при повторе возвращает уже сделанный результат. Правила повторов описаны в статье «Проектирование API для интеграций», они одинаковы для интеграций и для интерфейса.

Рассылка при росте числа пользователей

Пока сервис один, один сервер и десяток слушателей, всё работает почти любым способом. Проблемы появляются, когда растёт число одновременных соединений и число серверов.

  • Подписка по объекту, а не «на всё». Клиент слушает свой заказ, диспетчер — свою зону. Рассылка «всем обо всём» растёт с квадратом числа участников и быстро упирается в сервер.
  • Права проверяются при подписке. Человек не должен получить чужой заказ, подставив чужой номер. Проверка идёт на сервере, а не в интерфейсе.
  • Несколько серверов — общий канал. Если исполнитель подключён к одному серверу, а клиент к другому, событие должно дойти. Серверы обмениваются сообщениями через общую шину: Redis или очередь.
  • Частота ограничена. Позицию, которая на карте меняется еле заметно, не нужно слать каждую секунду. Шлём при заметном смещении или раз в несколько секунд, а на сервере объединяем близкие по времени сообщения.
  • Долгие соединения через прокси. Nginx и балансировщики по умолчанию закрывают простаивающее соединение. Таймауты настраиваются и проверяются отдельно, иначе поток обрывается «примерно через минуту».
  • Клиент тоже ограничен. Сотни маркеров, перерисовываемых каждую секунду, тормозят телефон: маркеры группируют.

Нагрузку проверяют не по числу пользователей, а по числу одновременных соединений и сообщений в секунду. Для высокой нагрузки это отдельная работа, см. статью «Микросервисы или монолит».

Хранение координат и приватность

Координаты человека — его персональные данные. Юридические вопросы (согласие, статус оператора, уведомления) решает юрист заказчика. Технические меры закладываются в проект сразу:

  • Для запросов «рядом» нужна пространственная база. PostgreSQL с расширением PostGIS и пространственным индексом отвечает на вопрос «какие точки в радиусе» намного быстрее, чем перебор. Если запросов мало, хватит простых широты и долготы с индексом.
  • Две таблицы: текущая позиция и маршрут. Текущая позиция перезаписывается, маршрут — только то, что нужно для разбора спорных выездов. Срок хранения записывается до запуска, удаление идёт по расписанию.
  • Точность снижается там, где можно. Для статистики и аналитики координаты округляют: три знака после запятой — квадрат порядка ста метров.
  • Доступ разделён. Клиент видит исполнителя только по своему заказу и пока заказ активен. Диспетчер видит больше, и каждый просмотр истории попадает в журнал действий.
  • Данные лежат там, где требует заказчик. Персональные данные граждан России обычно должны храниться на серверах в России, а внешний картографический сервис получает только то, что нужно для отрисовки.

Карты и поиск адресов дают внешние провайдеры (Яндекс Карты, 2ГИС, а также карты на основе OpenStreetMap). Условия, лимиты и стоимость определяет провайдер, договор и ключи оформляются на вас, мы подключаем и проверяем нагрузку.

Как проверить в поле

Лабораторная проверка не показывает половины проблем: ни дрейф точки между домами, ни поведение при погашенном экране. Перед запуском проверяем на реальных устройствах из вашего списка.

Что проверитьКакЧто считаем провалом
Отказ в разрешенииЗапретить доступ к позиции на каждом устройстве из спискаСервис не даёт выбрать точку вручную или показывает пустой экран
Погашенный экранПередавать позицию, заблокировать телефон на несколько минут, вернутьсяПосле возврата нет пометки о возрасте данных или поток не восстановился сам
Слабая связьПройти по маршруту через подвал, лифт, двор-колодецПриложение показывает старую точку как текущую или теряет отложенные действия
Обрыв потокаОтключить сеть на несколько секунд, включитьКлиент не перечитал состояние и показывает устаревший статус
Подмена позицииЭмулятор или специальное приложениеСервер принимает невозможный скачок без пометки
Расход батареиШесть-восемь часов смены, в фоне и на экранеТелефон не доживает до конца смены, поэтому исполнитель отключит сервис
Много одновременных соединенийНагрузочный прогон на обезличенных или сгенерированных данныхЗадержка вырастает, часть клиентов не получает события

Список устройств составляет заказчик по тем, что реально у людей. Проверять «все телефоны» невозможно, проверять названные — можно.

Что делаем мы и с чего начать

Начинаем с обследования: 200 000 ₽ и 2–3 недели за один-два процесса, 350 000 ₽ и 3–4 недели, если сервис связан с несколькими системами (учёт, склад, CRM). На выходе список сценариев, где нужна позиция и кто её передаёт, решение по способу доставки, ограничения браузера на ваших устройствах, схема хранения и вилка бюджета. Стоимость вычитается из первого этапа, если продолжаете с нами.

Дальше пилот одного сценария (например, карта выезда мастера) и первая версия, остановиться можно на любой ступени. Мы делаем веб-приложение, в том числе устанавливаемое на экран (PWA), но не нативные приложения и не фоновое слежение за людьми. Исходники лежат в вашем репозитории с первого коммита, круглосуточного дежурства у нас нет. Все цены — на странице «Тарифы», описание услуги — на странице «Real-time сервис с геолокацией».

Частые вопросы

Можно ли отслеживать курьера через веб-приложение?

Да, пока приложение открыто и экран включён: позиция приходит и уходит на сервер. При погашенном экране на телефоне обновления могут остановиться. Решения: держать телефон в держателе с включённым экраном, взять позицию из трекера в машине или переходить на нативное приложение, которое мы не делаем.

Чем SSE отличается от WebSocket?

SSE — поток событий только от сервера к браузеру, простой и с автоматическим переподключением. WebSocket двусторонний, подходит для диспетчерских экранов и команд, но переподключение и проверку живого соединения нужно писать самим. Если клиент только слушает, начинают с SSE.

Нужен ли WebSocket, если данные меняются раз в минуту?

Нет. Периодический опрос проще и надёжнее при такой частоте. Поток событий оправдан, когда изменения частые, а задержка в десятки секунд мешает.

Как проверить точность определения места?

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

Что делать, если пользователь запретил доступ к позиции?

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

Сколько хранить маршруты и координаты?

Столько, сколько нужно для разбора спорных случаев, срок записывают до запуска и применяют удалением по расписанию. Координаты человека — персональные данные, юридические вопросы решает юрист заказчика, технические меры закладываем мы.

Подойдёт ли PWA для такого сервиса?

Для большинства сценариев с открытым экраном подойдёт: установка на экран, кэш, уведомления. Ограничения: позиция в фоне и поведение уведомлений зависят от платформы. Поэтому проверка на ваших устройствах входит в обследование.

Сколько стоит разработка сервиса с картой и живыми статусами?

Обследование стоит 200 000 ₽ (350 000 ₽ при нескольких системах), лёгкий пилот — 150 000–400 000 ₽, первая версия — 400 000–1 200 000 ₽ за 2–3 месяца. Точную цену называем после обследования.