Веб-сервис может получать позицию пользователя и показывать другим людям изменения без перезагрузки страницы, но с границами. Браузер отдаёт координаты только странице по HTTPS и только с разрешения человека, а при погашенном экране на телефоне делает это ненадёжно. Обновления доставляют опросом, потоком событий от сервера (SSE) или через WebSocket, и способ выбирают по направлению и частоте данных, а не по моде.
Отсюда порядок решений. Сначала выясняем, кто и зачем передаёт позицию, потом выбираем способ доставки, храним состояние на сервере и считаем событие подсказкой. Как это выглядит в готовом продукте, описано на странице «Real-time сервис с геолокацией», здесь разбираем сами решения.
Что умеет браузер и чего не умеет
Позицию браузер отдаёт через стандартный программный интерфейс геолокации. Есть четыре условия, которые удобно знать до проектирования.
- Только по HTTPS и только с согласия. Страница без защищённого соединения позицию не получит. Согласие запрашивает сам браузер, и в некоторых браузерах оно действует только на текущий сеанс.
- Только пока страница живёт. Пока вкладка открыта и экран включён, позиция приходит. Когда экран гаснет или вкладка уходит в фон, обновления на телефоне могут остановиться, и это зависит от устройства и версии системы.
- Точность разная. Телефон с приёмником спутниковой навигации даёт десятки метров или лучше, компьютер определяет место по сетям Wi-Fi и может ошибиться на много сотен метров. В помещении, во дворах и в тоннеле картина хуже.
- Вместе с позицией приходит радиус погрешности. Его нужно сохранять и использовать, а не выбрасывать: точка с радиусом в километр и точка с радиусом в пять метров — разные данные.
Из второго пункта следует главное ограничение. Задача «непрерывно следить за курьером, пока телефон лежит в кармане с погашенным экраном» для веб-приложения не решается надёжно. Есть три выхода: исполнитель держит сервис открытым (телефон в держателе, экран включён, зарядка подключена), позиция приходит из другого источника, например из трекера в машине по его программному интерфейсу, или нужно нативное приложение. Нативные приложения мы не делаем, и об этом говорим до договора. Общий разбор выбора — в статье «PWA или нативное приложение».
Разрешение на позицию: состояния и что показывать
Запрос разрешения — самое частое место, где сервис теряет пользователя. Если запрашивать позицию сразу при открытии страницы, без объяснения, часть людей нажмёт «запретить», и браузер запомнит отказ. Поэтому сначала объясняем, зачем нужна позиция, и только потом вызываем системный запрос. Интерфейс держит пять состояний, и у каждого свой экран.
| Состояние | Что произошло | Что показываем | Чего не делаем |
|---|---|---|---|
| Не спрашивали | Пользователь только пришёл | Короткое объяснение и кнопка «Определить моё местоположение»; рядом ввод адреса | Не вызываем системный запрос при загрузке страницы |
| Разрешено | Позиция приходит | Точка на карте и радиус погрешности; кнопка остановить передачу | Не передаём позицию дальше, чем нужно сценарию |
| Отказано | Пользователь нажал «запретить» | Выбор точки на карте или ввод адреса и подсказка, как включить разрешение в настройках | Не просим снова при каждом открытии и не блокируем сервис |
| Недоступна | Ошибка или истёк срок ожидания, нет сигнала | Последнюю известную точку с временем и предложение повторить | Не рисуем старую точку как текущую |
| Грубая | Радиус погрешности слишком большой для задачи | Просьбу уточнить: выйти из здания, включить точную геолокацию, указать адрес | Не принимаем решение по такой точке молча |
Запасной путь через адрес нужен всегда. Сервис, который без позиции не работает, ломается у каждого, кто отказал или выключил геолокацию.
Точность: чему верить в координатах
Координаты с клиента — это его заявление. Для карты в приложении этого достаточно, для решений (засчитать выезд, открыть доступ, назначить заказ) нужна проверка на сервере. Типовые странности и что с ними делать:
| Симптом | Причина | Что делаем |
|---|---|---|
| Точка прыгает на сотни метров и возвращается | Устройство переключается между источниками: спутники, сети Wi-Fi, вышки | Отбрасываем точки с большим радиусом погрешности, сглаживаем маршрут на сервере, не сглаживаем «в ноль»: скорость и остановки должны остаться видны |
| Точка застыла | Экран погас, вкладка в фоне или пропала связь | Храним время последней точки и показываем возраст данных; по истечении срока помечаем «нет данных» |
| Первая точка далеко от настоящей | Первое определение быстрое и грубое, точная приходит позже | Ждём точку с приемлемым радиусом или показываем грубую с пометкой |
| Позиция «в океане» или в другом городе | Подмена на устройстве: эмулятор, расширение, специальное приложение | Проверяем правдоподобие: скорость между точками, согласие с адресом и сетью; для важных решений требуем второй признак, например фото на месте |
| Расстояние посчитано неверно | Расчёт по плоской формуле или перепутан порядок широты и долготы | Считаем расстояние по сфере или средствами пространственной базы и покрываем тестами с известными парами точек |
Если задача — отметка присутствия на объекте, допуск погрешности записываем в требования вместе с тем, что делать при его превышении. Спорные отметки уходят на проверку человеку.
Какой способ доставки обновлений выбрать
Способов три, и каждый хорош для своей задачи. Выбирают по трём вопросам: в какую сторону идут данные, как часто они меняются и сколько людей слушают одно и то же.
| Задача | Направление | Подходящий способ | Почему |
|---|---|---|---|
| Статус заказа раз в минуту | сервер клиенту, редко | Опрос по расписанию | Проще всего и достаточно; лишние запросы не страшны, пока страниц немного |
| Позиция курьера на карте клиента | сервер клиенту, часто | Поток событий (SSE) или WebSocket | Клиент только слушает, поток нужен постоянный; SSE проще, браузер переподключается сам |
| Передача позиции с телефона исполнителя | клиент серверу, часто | Обычные запросы или WebSocket | Одно направление; при редкой отправке хватает запросов, при частой удобнее одно соединение |
| Экран диспетчера с картой и командами | в обе стороны | WebSocket | Диспетчер и слушает, и отправляет команды, нужна задержка меньше секунды |
| Курьер подъезжает, пользователь закрыл страницу | сервер клиенту, разово | Push-уведомление | Приходит, когда сервис закрыт; зависит от платформы и разрешения, поэтому критичное дублируем |
Начинать стоит с самого простого способа, который выдерживает задачу. Опрос, который никто не заметил, лучше WebSocket, который никто не умеет сопровождать. Переход на поток событий делается позже и не требует переписывать остальное, если состояние хранится на сервере и читается одним запросом.
Событие и состояние: как не потерять изменения при обрыве
Главная ошибка real-time — считать, что сообщение дойдёт. Сеть на телефоне пропадает, вкладка засыпает, балансировщик обрывает простаивающее соединение. Поэтому событие не несёт «истину», оно говорит клиенту: «заказ изменился, перечитай». Как это строится:
- Состояние хранится на сервере. У заказа, очереди или исполнителя есть текущая запись в базе, которую можно получить одним запросом.
- У записи есть версия. Событие приходит с номером версии. Если клиент получил версию 7, а потом пришла 6 из-за гонки, она игнорируется.
- При подключении клиент сначала читает состояние, потом подписывается. Так он не пропустит изменение между запросом и подпиской.
- После обрыва клиент подключается заново и снова читает состояние. У SSE есть идентификатор последнего события, и сервер может дослать пропущенное; у WebSocket переподключение и запрос пропущенного пишем сами.
- Сервер проверяет, что соединение живо. Периодический служебный сигнал позволяет заметить «мёртвое» соединение, иначе оно висит и занимает память.
Повторная доставка не должна ничего ломать. Событие «заказ выдан» дважды не создаёт два документа, а команда «назначить исполнителя» при повторе возвращает уже сделанный результат. Правила повторов описаны в статье «Проектирование 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 месяца. Точную цену называем после обследования.