Магнитная Одиссея 2020: как Tarantool и Golang опутали всю Россию сетью экспресс-доставки

Всем привет, меня зовут Алексей Фельде, я IT архитектор направления Омниканальности в «Магните». Это молодое направление. Основная его цель — сформировать единый опыт офлайн и онлайн взаимодействия с покупателем.

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

Сервис экспресс-доставки стал первым в направлении Омни «Магнита». Поначалу мне казалось, что запуск такого онлайн-сервиса — классическая история с не менее классическим подходом к решению. Однако для ритейла, который более 25 лет затачивал свои бизнес-процессы под офлайн, запуск обернулся настоящей инженерной головоломкой.

В этой статье я расскажу, как в режиме speedrun небольшая инженерная команда запустила онлайн-сервис экспресс-доставки на Golang с помощью Tarantool Data Grid, gRPC и облака Mail.ru Cloud Solutions.

Алексей Фельде IT архитектор, взаимодействовал с мировыми e-commerce Мечтает выучить испанский языкАлексей Фельде IT архитектор, взаимодействовал с мировыми e-commerce Мечтает выучить испанский язык

В «Магнит» заходит пандемия, курьеры выходят из чата

Летом 2020 года перед проектной командой «Магнита» встала задача: за несколько месяцев с нуля запустить кнопку «Магнит Доставка» в смартфонах миллионов покупателей сети. И как я уже сказал, именно экспресс-доставка должна была стала первым элементом в цифровой экосистеме «Магнита».

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

Из грузовика в космолет: головоломка для IT инженера

Исходные вводные задачи: собрать актуальные данные об ассортименте в каждом магазине сети и отобразить их на онлайн-витрине.

Как решать? Можно, например, пойти по такой схеме: ритейлер формирует каталог с товарами магазинов, подключает их к сервису доставки и передает партнеру. И уже партнер отражает все это в приложении онлайн-витрины.

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

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

Отчет магазина за остатки товаров производится в конце дня. Днем же данные из магазина могут не поступать на центральный сервер — и это штатная ситуация. В то время как онлайн-витрина требует актуальной информации о товарных остатках и ценах каждую секунду.

Поэтому нам предстояло создать внутри «Магнита» централизованную систему хранения realtime-данных о товарах и их остатках в торговых точках, актуальных ценах и штрих-кодах.

Следующий необходимый для решения элемент — API к центральной системе, который должен стать middle-слоем между информационной системой «Магнита» и пользовательским приложением.

Выглядит все просто: в магазина стоит компьютер, который каждые пять минут синхронизирует данные об ассортименте и ценах с центральной системой хранения realtime-данных. Пользователь приложения «Магнит Доставка» видит витрину и заказывает доставку товаров. Курьер комплектует заказ на основе актуальных цен и количества товаров.

db442034ec76c2a457d6f514e423d1a8.jpg

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

Брюер и архитектура в «Магните»

Если рассматривать весь исторический архитектурный ландшафт «Магнита», то в основе находится центральное хранилище с данными для всех торговых точек.

Вокруг хранилища выстроено множество внутренних самописных систем, в которых идут операционные процессы: где-то крутятся алгоритмы ценообразования, где-то создаются акции и определяются скидки, где-то идет управление географией ассортимента.

Все данные из центрального хранилища и смежных сервисов разлетаются по торговым точкам. Каждая торговая точка по факту представляет из себя отдельный мини-ЦОД со своей инфраструктурой, базой данных, набором систем.

e0b0f204f7ae0bb992200cd5480e6462.jpg

Такой архитектурный ландшафт — классическая распределенная система, и она подчиняется CAP теореме Брюера. Она гласит, что при построении любой распределенной системы можно выбрать только 2 из 3-х свойств:

  • C (consistency) — согласованность. Каждое следующее прочтение даст вам самую последнюю запись;

  • A (availability) — доступность. Каждый узел (не упавший) всегда успешно выполняет запросы (на чтение и запись);

  • P (partition tolerance) — устойчивость к распределению. Даже если между узлами нет связи, они продолжают работать независимо друг от друга.

Когда торговые точки проектировали, то заложили два основных требования:

  • торговая точка изнутри должна быть доступна в любой момент на чтение и запись данных (availability);

  • торговая точка должна функционировать даже с потерей связи с центральным хранилищем (partition tolerance).

В этом случае можно было забыть о консистентности данных и принять допущение, что актуальные данные хранятся только в базе данных торговой точки. Конечно, мы стремимся к консистентности данных через обратную связь торговой точки с центральным хранилищем. Однако в конечном итоге мы достигаем согласованности с ощутимой задержкой по времени

Что нам стоит… центральное хранилище перестроить

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

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

  2. Высокая конкуренция запросов. Предлагаю вам при следующем посещении супермаркета понаблюдать и прикинуть конкуренцию оформления заказов внутри супермаркета. «Борьба за заказ» в оффлайн ограничена количеством открытых касс, в то время как в онлайн эта конкуренция ничем не лимитирована.

  3. Высокая доступность сервиса. Сервис должен быть отказоустойчивым — это must have для розницы. Ни природный катаклизм, ни обрыв связи с торговой точкой не должен помешать покупателю получить пакет с продуктами, косметикой, лекарствами.

Итак, вопрос: где надо внести изменения, чтобы гарантировать стабильность офлайн-процессов и обеспечить высокую скорость цифровизации? Допустим, мы решим внести изменения в историческое центральное хранилище данных.

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

  • Изменения тщательно продумываются и вносятся осторожно. Gartner в своей классификации систем определяет три группы, где каждой группе присущ свой срок использования и степень гибкости. Центральное хранилище входит в группу учетных систем и (логично) имеет низкую скорость изменений: его бесперебойная работа критически важна для всей компании.

8f31db3c48f95acfdc600a99dc0b7ae9.jpg

  • Профиль нагрузки от цифровых сервисов значительно интенсивнее профиля нагрузки от операционных процессов. Центральное хранилище годами выверяли и строили под специфику нагрузки операционных процессов. В свою очередь нагрузка от услуги экспресс-доставки требует высококонкурентный доступ и низкий отклик.

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

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

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

DIH vs ODS: выбор технологии

Команде разработки дали полный карт-бланш: раз строим что-то абсолютно новое, да еще в режиме speedrun, то можно и нужно экспериментировать. Все равно любая инновационная идея должна проходить стадию MVP, которая позволяет оперативно внедрить что-то новое, собирая на пути новые грабли и шишки, и также показав новые технологии и подходы в деле.

Чтобы сэкономить время, исследовали готовые архитектурные паттерны и обратили внимание на Digital Integration Hub (DIH). Если кратко, это архитектура приложений, выстроенная с целью агрегации данных с множества бэкендов. Она предоставляет эти данные множеству потребителей. По сути это современная противоположность Operational Data Store, но предназначенная для высоких нагрузок, так как обладает быстрым откликом и выдерживает высокую конкуренцию запросов.

84483a13f2924e9852dc3fcc63a68745.jpg

Сердце этой платформы — in-memory хранилище данных, в которое через интеграционный слой любым способом стекаются данные со всех бэкендов. Само in-memory хранилище предоставляет унифицированный API-доступ к данным, к которому подключаются остальные сервисы — как свои собственные, так и партнерские.

Как реализовать DIH? Есть множество решений на базе Apache Ignite, IBM Z и на базе SAP. Объединив усилия с B2B-командой Mail.ru, мы приступили к выбору подходящей под специфику «Магнита» технологии, а также к реализации необходимых архитектурных слоев.

Запускаем паука: как сделать обвязку из микросервисов

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

Чтобы оценить ее сложность, посчитаем в уме:

  1. потенциально максимальное количество магазинов — более 21 000;

  2. частота отставания данных между магазином и DIH — не более 5 минут;

  3. изменение остатков в отдельном магазине происходит ежеминутно. Для примера, один покупатель обслуживается за 30 секунд, средняя корзина — 5 товаров, а количество открытых касс, скажем, 2. Итого в минуту в одном магазине система регистрирует 20 изменений остатков. С учетом географии «Магнита» — это более 420 000 изменений в минуту, информация о которых поступает в единое хранилище данных;

  4. из прошлого пункта вытекает большой объем входящего потока данных в DIH с преобладанием write-операций.

Основная проблема, которую предстояло решить, — обработка большого количества RPS. На уровне интеграционного слоя перед in-memory хранилищем важно было сделать промежуточную обвязку в виде микросервисов, которые будут работать в памяти и принимать входящие потоки данных. В качестве языка выбирали между Python и Golang и в итоге выбрали Golang. Он отлично подходит для задач подобного плана, оптимально потребляет RAM\CPU и удобен для горизонтального масштабирования.

В качестве in-memory хранилища выбрали Tarantool. Описывать его преимущества нет необходимости, основные моменты здесь https://habr.com/ru/company/mailru/blog/562192/

Мы оценили удобство Tarantool: он оснащен множеством полезных инструментов и возможностью разместить микросервисы для работы с данными напрямую. Как раз те самые микросервисы интеграционного слоя через коннекторы сохраняют данные в Tarantool.

Для бесконечного масштабирования экземпляров «на живую» под любой нагрузкой используем Tarantool Data Grid. На базе Tarantool реализовали API Gateway, через который наши партнеры по доставке заказов Delivery Club и Яндекс.Еда забирают актуальный ассортимент магазина.

Уходим в облака, настраиваем протокол

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

Команда разработки бэкофиса магазина выбрала протокол gRPC. Он поддерживает более чем 11 языков (Java, C++, Ruby, Python и т.д.).

Какие преимущества gRPC попали в оценку:

  1. Вся API-интеграция сводилась к «удаленный сервер, получи эти данные и запиши себе», т.е. к RPC подходу;

  2. Данных в одну минуту передается много, без сжатия никак, бинарная передача данных — несомненный плюс gRPC;

  3. DIH создала сторонняя команда. Без коммуникации команд и без подхода API specification first тут не обойтись. Еще один плюс в копилку gRPC — сначала обговорили контракт обмена, задокументировали на уровне кода, затем быстро сгенерили серверную и клиентскую часть.

  4. Плюсом также стало то, что клиент и сервер могут быть написаны на разных языках. Это предоставляет много свободы.

image-loader.svg

Для развертывания сервиса подходит только облако. Только так можно не привязываться физически к одному ЦОДу и иметь возможность быстро закупать мощности под потенциально растущую нагрузку. Развернулись в Mail.ru Cloud Solutions, подняли защищенный канал коммуникации и настроили сетевые данные для новых потоков.

Итого в tech-корзине

Весь процесс от старта разработки и запуска первых торговых точек занял у нас около двух месяцев. Основной челлендж команды разработки бекофиса — научиться учитывать новые ограничения, с учетом текущего установленного софта, каналов связи и серверов в магазинах. После запуска и наблюдения в течение месяца пришлось на 60% пожертвовать в большую сторону интервалом синхронизации с DIH, чтобы этот поток негативно не повлиял на работу самого торгового объекта.

Наше решение пережило взрывное масштабирование — если в сентябре оно охватывало 20 магазинов, к октябрю их стало 200. Сейчас к сервису экспресс-доставки подключено 4000 торговых точек по всей России. Нам предстоит полное масштабирование по географии «Магнита». На текущий момент мы обрабатываем более 150 000 изменений в секунду.

Каждые 8 минут магазины обновляют актуальную информацию о наличии товаров и ценах на все позиции в системе. Все эти данные подгружаются из хранилища по защищенному каналу в Tarantool. Таким же образом система загружает информацию о заказах. Затем данные агрегируются, после чего передаются по интеграционным сервисам на доставку. Курьер получает информацию о заказе и идет в магазин на сборку.

Экспресс-доставка стала нашим дебютом среди онлайн-сервисов в цифровой экосистеме «Магнита». А как бы вы решили задачу запуска онлайн-сервиса для классического и очень большого ритейлера в режиме speedrun?

© Habrahabr.ru