Привет, Хабр. Сегодня расскажем про взрослый highload, надёжность и инженерный подход к решению сложных технических задач.
В каждой компании, особенно крупной, есть необходимость в различных геосервисах: картах, маршрутах, геокодировании, измерении расстояний и так далее. В Сбере на этом строится огромное количество бизнес‑процессов: построение оптимальных маршрутов инкассаторов, анализ клиентопотока и доступности транспорта для определения наилучшего места для открытия офиса, планирование тарифов для услуг инкассации, планирование расходов ГСМ и технического обслуживания транспорта, доставка банковских карт, отслеживание нахождения мобильных офисов, геокодирование адресов в карточках клиентов, анализ конкурентов по тысячам метрик, включая геометрики, и ещё сотни и сотни других процессов.
Умножим это на масштабы всей страны, и на выходе получим около 3000-5000 запросов в секунду к Геоинформационной системе.
Для Сбера критически важно, чтобы такая система была развёрнута внутри банка по двум причинам:
Есть конфиденциальные данные, которые нельзя отдавать за периметр банка в чужие облака.
Есть определённые требования к надёжности, которые можно соблюсти только в периметре банка.
До перехода на новую геоплатформу мы использовали ArcGIS On-Premise — комплекс геоинформационных программных продуктов американской компании ESRI. Пару лет назад мы начали изучать альтернативы на рынке. Под наши требования подходил только 2ГИС On-Premise. Основными критериями были:
Команда 2ГИС уже имеет богатый опыт, и они были готовы доработать продукт под наши запросы, нашу нагрузку и SLA.
Есть возможность развернуть геоплатформу в инфраструктуре Сбера и обеспечить её работу внутри периметра, полностью локально.
Входит в Реестр российского программного обеспечения.
Выбираем подход
Итак, нам нужно было перевести больше 50 систем-потребителей с ArcGIS On-Premise на 2ГИС On-Premise. Масштаб систем разный: кто-то делает 100-200 запросов в день, кто-то рассчитывает за сутки 400 миллионов маршрутов, кто-то шлёт 1000 запросов в секунду на геокодирование. И нужно было переключить всех так, чтобы ни один бизнес-процесс не пострадал. Всё это за ограниченное количество времени.
У нас, как у команды-владельца Геоинформационных систем в Сбере, было два способа решить задачу:
Административный. Поставить задачу всем системам, чтобы они переписали свой код под новый API другой ГИС.
Инженерный. Никого не беспокоить, самостоятельно придумать способ перевести всех.
Административный вариант был слишком дорогим и неэффективным для нас. Мы утонули бы в коммуникациях, разработках и релизах, поломали бы беклоги всем командам и в конечном итоге сорвали бы все сроки. Поэтому начали решать проблему инженерно. Задачей максимум было переключить всех так, чтобы никто даже не заметил.

Разворачиваем новый кластер
Первым делом нужно было собрать новый отказоустойчивый кластер с надежностью 99,95 % (это не более 43 секунд простоя в день), который продолжал бы работать в любых ситуациях, включая полную недоступность ЦОДа. Получилась такая схема:

Получилось четыре плеча новой геоплатформы, из которых собраны две геораспределённые пары: одна рабочая активная, другая резервная. Переключение между геопарами вручную одной кнопкой либо автоматически при срабатывании определённых триггеров.
Я на схеме намеренно для простоты убрал всю инфраструктурную обвязку в виде брокеров, баз данных и прочего. Давайте считать, что это всё прячется за квадратиком «2ГИС». Все эти компоненты тоже резервированы и геораспределены.
При сборке кластера везде использовали продукты из состава Platform V:
Kafka заменили на Platform-V Corax;
PostgreSQL на Platform V Pangolin DB;
Nginx на Platform V SynGX;
Cassandra на Platform V Distributed DB;
Ceph на Platform V S-UP;
Alteon на LBOSSE.
Готовим план переключения
Сначала определимся с терминами, дальше я буду часто их упоминать:
Растровые и векторные карты: карты, которые мы видим глазами.
Геокодирование прямое и обратное: когда текстовый адрес преобразуется в координаты и наоборот.
Маршрут: путь из точки А в точку Б на каком-нибудь транспорте или пешком.
Изохроны: область на карте, в которую можно доехать за N минут из выбранной точки. Используются для анализа конкурентов и выбора оптимального места открытия офиса
Матрицы времени и расстояний: матрицы N x M, в которых для каждого пересечения нужно посчитать расстояние и время в пути. То есть для матрицы 1000×1000 нужно сосчитать 1 миллион маршрутов. Матрицы используются, например, для выбора оптимального маршрута объезда точек инкассации.
POI: cправочник всех организаций в разных разрезах.
Это основные сервисы, которыми пользуются наши системы-потребителей:

Затем сравнили ключевые сервисы предыдущего и нового поставщиков: на 99 % API можно было конвертировать без потери данных. Глобально весь API можно поделить на три группы:
Простая конвертация синхрон-синхрон + правильный маппинг полей.
Оборачиваем синхрон в псевдоасинхрон или наоборот.
Несколько последовательных вызовов разных конечных точек объединяем в один вызов.
Для всех групп мы разработали конвертеры API.
Переводим весь трафик на новую геоплатформу
Задумались над способом бесшовного переключения потребителей на эти конвертеры. Для этого нужно было решить две задачи: как завернуть весь трафик на конвертеры и подменить потребительские ключи ArcGIS на ключи 2ГИС On-Premise.
На конвертеры трафик завернули, изменив немного схему маршрутизации на балансировщиках и промежуточных SynGX. После этого сменили на DNS-сервере IP-адрес балансировщика Alteon из контура ArcGIS на IP-адрес балансировщика LBOSSE из контура 2ГИС. А на SynGX после балансировщика уже настроили маршруты, позволяющие оперативно менять направление трафика между платформами.
С ключами было чуть сложнее. Для работы с API 2ГИС On‑Premise для каждого потребителя генерируется свой API_KEY. Мы, конечно, могли на конвертерах «зашить» технический API_KEY, общий для всех, но нам важно было управлять каждым потребителем отдельно. Поэтому разработали ещё один промежуточный адаптер, который подменял ключи, анализируя URL запроса.
Запросы в ArcGIS шли таким образом, что для каждого потребителя был свой уникальный URL формата /arcgis/system_name/service_name, поэтому мы могли явно сопоставить потребителя из URL и API_KEY 2ГИС:

После этого начали постепенно переключать на SynGX потребителей одного за другим на эти конвертеры, начиная с самых маленьких. За три месяца мы переключили 95 % потребителей.
Упрощённо схема получилась такой:

Всё, что заметили потребители, — поменялся цвет карты и она стала более подробной. Ну и геокодер стал поумнее и пошустрее.
Переключаем самых крупных потребителей
Основные технические сложности начались при переключении трёх крупных потребителей сервиса матриц расстояний:
-
Аналитический центр, который в своих расчётах использует расстояния фактически из каждого здания до каждого в стране. Им нужны вообще все возможные существующие маршруты, потому что с их помощью рассчитываются тарифы на инкассацию, оптимальные маршруты, расход ГСМ и ещё сотни разных показателей.
Чтобы не рассчитывать абсолютно все расстояния между всеми домами в стране, они рассчитывают расстояния между центрами гексов. Гекс — это укрупнённая область на карте. Благодаря такой оптимизации получается на несколько порядков меньше маршрутов, но с погрешностью в 100–200 метров в зависимости от размера гексов.

Гексы на карте объединяют несколько домов в одну расчётную область
-
Инкассаторы. Тут решается классическая задача коммивояжера: есть N точек инкассации и M машин, и надо объехать все точки инкассации оптимальным способом. Тоже очень много расчётов матриц расстояний. И когда они не рассчитываются у людей в бронированных машинах с автоматами, разработчики начинают нервничать :)

Граф всех возможных маршрутов и поиск оптимального Сервис доставки банковских карт. Задача аналогична предыдущей, но ещё добавляется возможность использования общественного транспорта, что само по себе усложняет расчёт графа расстояний. А так как в некоторых случаях карты мы доставляем в день заказа, считать это всё надо быстро.
Глобально мы столкнулись с тремя типами проблем при переключении потребителей:
Нам не хватало производительности для расчёта такого объёма матриц в рамках нужного SLA.
Потребители конфликтовали между собой, отнимая друг у друга ресурсы.
Потребители конфликтовал сами с собой, пытаясь одновременно рассчитывать разные типы матриц.
Первое и самое очевидное, что мы попробовали сделать — это горизонтальное масштабирование в рамках существующего кластера из четырёх плеч. Мы добавили поды, накинули дополнительные процессоры, и в целом это помогало увеличивать скорость расчёта, но нелинейно.
Можно было бы на каждого потребителя выделить отдельный кластер геоплатформы, или даже несколько кластеров, как это было у нас с ArcGIS: там для Инкассаторов был свой кластер, для Аналитической платформы — свой. Таким образом мы сняли бы влияние одних потребителей на других.
Но в какой‑то момент мы остановились и поняли, что проблему надо решать не аппаратно, заливая железом, а архитектурно. Тем более, что одной из целью перехода было снижение стоимости владения продуктом.
Придумываем решение
Проектная команда начала разрабатывать план решения по каждому типу проблем.
Не хватало производительности для расчёта
Под капотом новая геоплатформа рассчитывала матрицы таким образом:

То есть вся матрица целиком уходила в работу одному экземпляру сервиса. При такой схеме одна матрица 1000х1000 (это миллион маршрутов) на нашем кластере считается примерно 10 минут. За счёт распараллеливания мы за те же 10 минут могли получить условно 10 матриц на 10 экземплярах сервиса.
Проектная команда провела глубокий анализ и, смоделировав различные архитектуры, пришла к выводу, что нам выгоднее делать так:

То есть сначала дробим матрицу на более мелкие и распределяем на все свободные экземпляры сервиса. Тогда матрица 1000х1000 на 10 экземплярах сервиса считается уже за 1 минуту, но при этом 10 матриц будут считаться всё за те же 10 минут, что и в предыдущей архитектуре. Как может показаться, при высокой нагрузке мы получаем тоже самое, но всё-таки есть одно существенное отличие: чем быстрее геоплатформа посчитает каждую матрицу, тем быстрее мы перенесём расчёты с нашего кластера либо на легче масштабируемые кластеры постобработки, либо сразу на серверы потребителей для обработки результата. Таким образом мы сглаживаем график потребления серверов постобработки и раньше вовлекаем их в работу без пиковых скачков. Наглядно:

Конфликт потребителей
Суть проблемы: приходит условная Аналитическая платформа, которой надо посчитать 2000 матриц, отгружает нам в геоплатформу эти матрицы и уходит спокойно ждать результат; в это же время Инкассаторам нужно оперативно рассчитать маршруты, но они не могут этого сделать, потому что вся очередь геоплатформы забита матрицами Аналитической платформы. Получается, что кто-то из потребителей всегда отъедает весь ресурс геоплатформы и другие вынуждены ждать.
Либо бывает, что потребитель сам у себя отъедает ресурс. Например, у Инкассаторов есть три типа расчётов:
Регулярное обновление всего графа по региону раз в сутки. В этом случае прилетает 150–200 матриц 1000×1000 за раз.
Дельты каждую ночь по всем регионам, это 1000–1500 матриц разного размера, вплоть до 500×500.
Внутридневные расчёты, когда надо в моменте перестроить маршрут — 2000–3000 матриц размером вплоть до 50×50.
И вот случается, что прилетели матрицы обновления всего графа дорог по региону, и в это же время надо посчитать внутридневные матрицы в другом регионе. Возникает конфликт одного потребителя: мы для него уже считаем матрицы 1000×1000, поэтому матрица 50×50 ждёт в очереди, хотя могла бы быть посчитана за 5 секунд.
Мы поняли, что нам нужен некий умный балансировщик матриц потребителей:

Этот балансировщик должен был:
Повторять API сервиса матриц.
Аккумулировать все матрицы от всех потребителей у себя в базе данных.
Ограничивать количество матриц, одновременно рассчитываемых серверами 2ГИС. Это позволяет часть ресурсов кластера держать всегда свободными для более приоритетных матриц.
Раз в секунду анализировать очередь и готовить следующи�� батч в зависимости от десятка разных параметров: размерности матриц, времени суток, сколько матриц по каждому потребителю уже было посчитано минутой ранее, текущих матриц в расчёте и так далее.
Распределять нагрузку между синхронным и асинхронным сервисом под капотом новой геоплатформы. Синхронный API служит для быстрого расчёта матриц размерностью до 30х30, асинхронный — для всего остального. Так исторически сложилось, что все наши потребители всегда пользовались асинхронным API и для больших матриц, и для маленьких. Балансировщик должен был анализировать размер матриц и сам перераспределять маленькие матрицы на синхронный сервис.
Вычислять хеш матрицы и не считать её повторно. Наш анализ показал, что иногда в течение 48 часов приходили одинаковые матрицы, поэтому мы добавили функцию, которая не отправляла бы дубли снова в геоплатформу, а сразу выдавала готовый расчёт, если он не старше двух дней.
Пока команда 2ГИС меняла архитектуру, мы разработали этот балансировщик. Итоговая схема получилась такая:

Результаты
После запуска мы несколько недель настраивали на умном балансировщике алгоритм определения следующего батча, чтобы не обидеть никого из потребителей. В итоге подобрали хороший вариант автобалансировки.
Сейчас мы уже второй квартал работаем полностью на новом решении, в январе полностью вывели из эксплуатации серверы ArcGIS.
Текущее решение работает объективно быстрее и качественнее, чем ArcGIS: карты и граф дорог точнее, обновление регулярное, есть очень детальный справочник организаций, геокодер работает точнее, матрицы и маршруты учитывают свежие данные по пробкам и перекрытиям, совокупная скорость расчёта матриц выше в 2-3 раза. Для сравнения: раньше Инкассаторы на ArcGIS ждали полного расчёта графа дорог по одному региону где-то 5-6 часов, и в это время сильно падала скорость внутридневных расчётов, а сейчас полный расчёт этого же региона занимает 1,5-2 часа без влияния на внутридневные расчёты. На ArcGISчасти сервисов вообще не было, например, сервиса расчёта матриц расстояний для общественного транспорта уникальный, у него нет аналогов.
Немного статистики по нагрузке главных сервисов:
Растровые и векторные карты: ~100-200 запросов в секунду.
Геокодирование: ~1800 запросов в секунду.
Матрицы расстояний: ~20 матриц в минуту разных размеров от 10х10 до 1000х1000. Это где-то совокупно 200-500 миллионов маршрутов в сутки, что эквивалентно 2500-5800 маршрутов в секунду.
Спасибо!
Комментарии (16)

Anton_673
04.03.2026 09:06Основными критериями были:
0. 2ГИС принадлежит Сберу.

ruslan_z Автор
04.03.2026 09:06На самом деле при выборе таких решений этот критерий вообще не учитывается - слишком много поставлено на кон, чтобы рисковать бизнесом ради "зато наше".
Можете провести независимый анализ конкурентов и поделиться своими наблюдениями, почему не 2ГИС, а кто-то другой должен был быть.
Abyss777
А скажите пожалуйста, а нельзя ли все эти матрицы предрассчитать и хранить? Адреса по карте вроде не бегают. Дороги новые тоже не часто строят. Пробки имеют точность +-километр, можно и среднее брать.
ruslan_z Автор
В России сейчас насчитывается около 120 млн адресов. Если посчитать матрицу заранее по всем, то это получится 120 000 000 х 120 000 000 = 14 400 000 000 000 000 уникальный маршрутов (я даже не знаю, как это число называется =)). Чтобы это посчитать, надо ~2 853 881 лет непрерывной работы кластера.
Даже если считать отдельно по каждому городу, то задача все равно нерешаемая. В Мск, например, около 3,5-5 млн адресов.