С 2019 года в России действует закон об обязательной маркировке.
Закон распространяется не на все группы товаров, а сроки вступления в силу обязательной маркировки для групп товаров разные.
Первыми обязательной маркировке будут подвергнуты табак, обувь и лекарства; другие товары будут добавлены позже, например, парфюмерия, текстиль и молоко.
Данное законодательное нововведение побудило к разработке новых IT-решений, которые позволят отслеживать всю жизненную цепочку продукта от производства до покупки конечным потребителем, всем участникам процесса: как самому государству, так и всем организациям, реализующим товары с обязательная маркировка.
В Х5 система, которая будет отслеживать маркированные товары и обмениваться данными с государством и поставщиками, называется «Маркус».
Расскажем по порядку, как и кто его разработал, каков его технологический стек и почему нам есть чем гордиться.
Реальная высокая нагрузка
«Маркус» решает множество задач, главная из которых — интеграционное взаимодействие информационных систем Х5 и государственной информационной системы маркированной продукции (ГИС МП) для отслеживания движения маркированной продукции.Платформа также хранит все полученные нами коды маркировки и всю историю перемещения этих кодов по объектам и помогает исключить пересортицу маркированной продукции.
На примере табачных изделий, вошедших в первые группы маркированных товаров, всего в одном грузовике сигарет содержится около 600 тысяч пачек, каждая из которых имеет свой уникальный код. И задача нашей системы — отслеживать и проверять легальность перемещения каждой такой упаковки между складами и магазинами, а в конечном итоге проверять допустимость их продажи конечному покупателю.
А мы фиксируем около 125 000 кассовых операций в час, а еще нам необходимо фиксировать, как каждая такая пачка попала в магазин.
Таким образом, учитывая все перемещения между объектами, нас ожидают десятки миллиардов записей в год.
Команда М
Несмотря на то, что Marcus считается проектом внутри Х5, он реализуется с использованием продуктового подхода.Команда работает по Scrum. Проект стартовал летом прошлого года, но первые результаты появились только в октябре — была полностью собрана собственная команда, разработана архитектура системы и закуплено оборудование.
Сейчас в команде 16 человек, шесть из которых занимаются бэкенд- и фронтенд-разработкой, трое — системным анализом.
Еще шесть человек занимаются ручным, нагрузочным, автоматизированным тестированием и обслуживанием продукта.
Кроме того, у нас есть специалист по SRE. В нашей команде код пишут не только разработчики; почти все ребята умеют программировать и писать автотесты, скрипты загрузки и скрипты автоматизации.
Мы уделяем этому особое внимание, поскольку даже поддержка продуктов требует высокого уровня автоматизации.
Мы всегда стараемся проконсультировать и помочь коллегам, которые раньше не занимались программированием, и дать им небольшие задачи для работы.
В связи с пандемией коронавируса мы перевели всю команду на удаленную работу; наличие всех инструментов для управления разработкой, выстроенный рабочий процесс в Jira и GitLab позволили легко пройти этот этап.
Месяцы, проведенные удаленно, показали, что продуктивность команды от этого не пострадала; для многих повысился комфорт в работе, не хватало только живого общения.
Удаленная встреча команды
Встречи во время удаленной работы
Технологический стек решения
Стандартным репозиторием и инструментом CI/CD для X5 является GitLab. Мы используем его для хранения кода, непрерывного тестирования и развертывания на тестовых и производственных серверах.Также мы используем практику code review, когда минимум 2 коллеги должны одобрить изменения, внесенные разработчиком в код. Статические анализаторы кода SonarQube и JaCoCo помогают нам поддерживать чистоту кода и обеспечивать необходимый уровень покрытия модульными тестами.
Все изменения в коде должны пройти эти проверки.
Все тестовые сценарии, запускаемые вручную, впоследствии автоматизируются.
Для успешной реализации бизнес-процессов «Маркусу» нам пришлось решить ряд технологических задач, о каждой по порядку.
Задача 1. Необходимость горизонтальной масштабируемости системы Для решения этой проблемы мы выбрали микросервисный подход к архитектуре.
При этом очень важно было понимать зоны ответственности служб.
Мы постарались разделить их на бизнес-операции, учитывая специфику процессов.
Например, приемка на складе – это не очень частая, но очень масштабная операция, в ходе которой необходимо быстро получить от государственного регулятора информацию о единицах принимаемого товара, количество которых в одной поставке достигает 600 000. , проверим допустимость принятия данного товара на склад и предоставим всю необходимую информацию для системы автоматизации склада.
А вот отгрузка со складов имеет гораздо большую интенсивность, но при этом оперирует небольшими объемами данных.
Мы реализуем все сервисы без сохранения состояния и даже пытаемся разделить внутренние операции на этапы, используя то, что мы называем автотемами Kafka. Это когда микросервис отправляет сообщение самому себе, что позволяет балансировать нагрузку на более ресурсоемкие операции и упрощает обслуживание продукта, но об этом позже.
Мы решили выделить модули взаимодействия с внешними системами в отдельные сервисы.
Это позволило решить проблему частого изменения API внешних систем, практически не отразившись на сервисах с бизнес-функциональностью.
Все микросервисы развертываются в кластере OpenShift, что решает как проблему масштабирования каждого микросервиса, так и позволяет не использовать сторонние инструменты Service Discovery. Задача 2. Необходимость поддержания высокой нагрузки и очень интенсивного обмена данными между сервисами платформы: Только на этапе запуска проекта выполняется около 600 операций в секунду.
Мы ожидаем, что это значение увеличится до 5000 операций в секунду по мере подключения торговых точек к нашей платформе.
Эту проблему решили за счет развертывания кластера Kafka и практически полного отказа от синхронного взаимодействия микросервисов платформы.
Это требует очень тщательного анализа системных требований, поскольку не все операции могут быть асинхронными.
При этом мы не только передаем события через брокера, но и передаем в сообщении всю необходимую бизнес-информацию.
Таким образом, размер сообщения может достигать нескольких сотен килобайт. Ограничение размера сообщений в Kafka требует от нас точно прогнозировать размер сообщений и при необходимости разделять их, но деление логичное, связанное с бизнес-операциями.
Например, мы делим товары, которые приезжают на машине, на коробки.
Для синхронных операций выделяются отдельные микросервисы и проводится тщательное нагрузочное тестирование.
Использование Kafka поставило перед нами еще одну задачу — тестирование работы нашего сервиса с учетом интеграции Kafka делает все наши модульные тесты асинхронными.
Мы решили эту проблему, написав собственные служебные методы с использованием Embedded Kafka Broker. Это не избавляет от необходимости писать модульные тесты для отдельных методов, но сложные случаи мы предпочитаем тестировать с помощью Kafka. Большое внимание было уделено трассировке логов, чтобы их TraceId не терялся при возникновении исключений во время работы сервисов или при работе с пакетом Kafka. И если с первым особых проблем не было, то во втором случае мы вынуждены логировать все TraceIds, с которыми пришел пакет, и выбирать один для продолжения трассировки.
Тогда при поиске по исходному TraceId пользователь легко узнает, с чего продолжилась трассировка.
Задача 3. Необходимость хранения большого объема данных: Только на табачные изделия в Х5 поступает более 1 миллиарда этикеток в год. Они требуют постоянного и быстрого доступа.
Всего система должна обработать около 10 миллиардов записей истории движения этих маркированных товаров.
Для решения третьей проблемы была выбрана NoSQL-база данных MongoDB. Мы создали сегмент из 5 узлов, и каждый узел имеет набор реплик из 3 серверов.
Это позволяет масштабировать систему по горизонтали, добавляя в кластер новые серверы, и обеспечивать ее отказоустойчивость.
Здесь мы столкнулись с еще одной проблемой — обеспечением транзакционности в монго-кластере с учетом использования горизонтально масштабируемых микросервисов.
Например, одна из задач нашей системы — выявление попыток перепродажи продукции с одинаковыми кодами маркировки.
Здесь появляются накладки при ошибочных сканах или ошибочных операциях кассиров.
Мы обнаружили, что такие дубликаты могут встречаться как в одном обрабатываемом пакете Kafka, так и в двух параллельно обрабатываемых пакетах.
Таким образом, проверка дубликатов путем запроса к базе данных ничего не дала.
Для каждого микросервиса мы решали задачу отдельно, исходя из бизнес-логики этого сервиса.
Например, для проверок мы добавили проверку внутри пакета и отдельную обработку появления дубликатов при вставке.
Чтобы работа пользователей с историей операций никоим образом не влияла на самое главное — функционирование наших бизнес-процессов, мы вынесли все исторические данные в отдельный сервис с отдельной базой данных, которая также получает информацию через Kafka. .
Таким образом, пользователи работают с изолированным сервисом, не затрагивая сервисы, обрабатывающие данные для текущих операций.
Задача 4. Повторная обработка и мониторинг очереди: В распределенных системах неизбежно возникают проблемы и ошибки с доступностью баз данных, очередей и внешних источников данных.
В случае с Маркусом источником таких ошибок является интеграция с внешними системами.
Необходимо было найти решение, которое бы позволяло повторять запросы на ошибочные ответы с некоторым заданным таймаутом, но при этом не прекращало обработку успешных запросов в основной очереди.
Для этой цели была выбрана так называемая концепция «повторной попытки по теме».
Для каждой основной темы создается одна или несколько тем повтора, в которые отправляются ошибочные сообщения и при этом устраняется задержка обработки сообщений из основной темы.
Схема взаимодействия -
Для реализации такой схемы нам нужно было следующее: интегрировать это решение со Spring и избежать дублирования кода.
Путешествуя по сети, мы наткнулись на похожее решение на основе Spring BeanPostProccessor, но оно показалось нам излишне громоздким.
Наша команда сделала более простое решение, которое позволяет нам интегрироваться в цикл Spring для создания потребителей и дополнительно добавлять Retry Consumers. Мы предложили команде Spring прототип нашего решения, вы можете его увидеть здесь .
Количество Retry Consumers и количество попыток для каждого потребителя настраиваются через параметры в зависимости от потребностей бизнес-процесса, и чтобы все работало, остается только добавить аннотацию org.springframework.kafka.annotation.KafkaListener , который знаком всем разработчикам Spring. Если сообщение не удалось обработать после всех повторных попыток, оно отправляется в DLT (тему недоставленных писем) с использованием Spring DeadLetterPublishingRecoverer. По просьбе поддержки мы расширили этот функционал и создали отдельный сервис, позволяющий просматривать сообщения, включенные в DLT, stackTrace, TraceId и другую полезную информацию о них.
Кроме того, во все темы DLT были добавлены мониторинг и оповещения, и теперь фактически появление сообщения в теме DLT — это повод проанализировать и исправить дефект. Это очень удобно – по названию темы мы сразу понимаем, на каком этапе процесса возникла проблема, что существенно ускоряет поиск ее первопричины.
Совсем недавно мы реализовали интерфейс, позволяющий повторно отправлять сообщения с помощью нашей поддержки после устранения их причин (например, восстановления работоспособности внешней системы) и, конечно же, установления соответствующего дефекта для анализа.
Вот тут-то и пригодятся наши собственные темы; чтобы не перезапускать длинную цепочку обработки, можно перезапустить ее с нужного шага.
Эработа платформы
Платформа уже работает продуктивно, каждый день мы осуществляем поставки и отгрузки, подключаем новые распределительные центры и магазины.В рамках пилота система работает с товарными группами «Табак» и «Обувь».
Вся наша команда участвует в проведении пилотов, анализирует возникающие проблемы и вносит предложения по улучшению нашего продукта, от улучшения логов до изменения процессов.
Чтобы не повторять наших ошибок, все случаи, выявленные в ходе пилота, отражаются в автоматизированных тестах.
Наличие большого количества автотестов и модульных тестов позволяет провести регрессионное тестирование и установить исправление буквально за несколько часов.
Сейчас мы продолжаем развивать и совершенствовать нашу платформу и постоянно сталкиваемся с новыми задачами.
Если вам интересно, мы расскажем о наших решениях в следующих статьях.
Теги: #ИТ-инфраструктура #разработка #Микросервисы #Управление продуктом #тестирование #Анализ и проектирование систем #kafka #highload #agile #код #высоконагруженные проекты #scrum #анализатор кода #команда #информационная система #маркировка продуктов #кассовые операции #поставщики # поставщиков
-
Ии. Тактический Барьерный Шагоход
19 Oct, 24 -
Моба Здорового Человека (Концепция)
19 Oct, 24 -
Доступны Пробные Версии Cs5
19 Oct, 24 -
Что Необходимо Знать Разработчикам Ios
19 Oct, 24