„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Od 2019 r. w Rosji obowiązuje ustawa dotycząca obowiązkowego etykietowania. Prawo nie dotyczy wszystkich grup towarów, a daty wejścia w życie obowiązkowego oznakowania dla grup towarów są różne. W pierwszej kolejności obowiązkowemu oznakowaniu zostaną objęte tytoń, obuwie i leki, później dodane zostaną inne produkty, np. perfumy, tekstylia i mleko. Ta innowacja legislacyjna spowodowała rozwój nowych rozwiązań informatycznych, które umożliwią prześledzenie całego łańcucha życia produktu od produkcji do zakupu przez konsumenta końcowego, wszystkim uczestnikom tego procesu: zarówno samemu państwu, jak i wszystkim organizacjom sprzedającym towary z obowiązkowe oznakowanie.

W X5 system, który będzie śledził oznakowane towary i wymieniał dane z państwem i dostawcami, nazywa się „Marcus”. Opowiedzmy po kolei, jak i kto go opracował, jaki jest jego stos technologiczny i dlaczego mamy się czym pochwalić.

„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Prawdziwe duże obciążenie

„Marcus” rozwiązuje wiele problemów, z których najważniejszym jest interakcja integracyjna pomiędzy systemami informacyjnymi X5 a systemem informacji o stanie produktów oznakowanych (GIS MP) w celu śledzenia przemieszczania się oznakowanych produktów. Platforma przechowuje także wszystkie otrzymane przez nas kody etykietowe oraz całą historię przemieszczania się tych kodów pomiędzy obiektami, co pozwala wyeliminować przeklasyfikowanie oznakowanych produktów. Na przykładzie wyrobów tytoniowych, które zaliczały się do pierwszych grup oznakowanych towarów, już w jednej ciężarówce papierosów mieści się około 600 000 paczek, z których każda ma swój niepowtarzalny kod. A zadaniem naszego systemu jest śledzenie i weryfikacja legalności przemieszczania się każdej takiej paczki pomiędzy magazynami i sklepami, a docelowo weryfikacja dopuszczalności ich sprzedaży końcowemu nabywcy. Odnotowujemy około 125 000 transakcji gotówkowych na godzinę i musimy też rejestrować, w jaki sposób każda taka paczka dostała się do sklepu. Zatem biorąc pod uwagę wszystkie ruchy pomiędzy obiektami, spodziewamy się dziesiątek miliardów rekordów rocznie.

Zespół M

Pomimo tego, że Marcus jest uważany za projekt w ramach X5, jest on realizowany w oparciu o podejście produktowe. Zespół pracuje według Scruma. Projekt ruszył latem ubiegłego roku, ale pierwsze efekty przyszły dopiero w październiku – skompletowano własny zespół, opracowano architekturę systemu i zakupiono sprzęt. Teraz zespół liczy 16 osób, z czego sześć zajmuje się rozwojem backendu i frontendu, z czego trzy zajmują się analizą systemu. Sześć kolejnych osób zajmuje się testowaniem ręcznym, testowaniem obciążenia, automatycznym i konserwacją produktu. Dodatkowo posiadamy specjalistę SRE.

W naszym zespole nie tylko programiści piszą kod, prawie wszyscy wiedzą, jak programować i pisać autotesty, skrypty ładowania i skrypty automatyzacji. Zwracamy na to szczególną uwagę, ponieważ nawet obsługa produktu wymaga wysokiego stopnia automatyzacji. Zawsze staramy się doradzić i pomóc kolegom, którzy wcześniej nie programowali, i zlecić im kilka drobnych zadań do pracy.

W związku z pandemią wirusa przenieśliśmy cały zespół do pracy zdalnej, dostępność wszystkich narzędzi do zarządzania rozwojem, zbudowany przepływ pracy w Jira i GitLab pozwoliły z łatwością przejść ten etap. Miesiące spędzone zdalnie pokazały, że w rezultacie nie ucierpiała produktywność zespołu, dla wielu wzrósł komfort pracy, brakowało jedynie komunikacji na żywo.

Zdalne spotkanie zespołu

„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Spotkania podczas pracy zdalnej

„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Stos technologiczny rozwiązania

Standardowym repozytorium i narzędziem CI/CD dla X5 jest GitLab. Używamy go do przechowywania kodu, ciągłego testowania i wdrażania na serwery testowe i produkcyjne. Stosujemy również praktykę przeglądu kodu, gdy co najmniej 2 współpracowników musi zatwierdzić zmiany wprowadzone w kodzie przez programistę. Statyczne analizatory kodu SonarQube i JaCoCo pomagają nam utrzymać nasz kod w czystości i zapewniają wymagany poziom pokrycia testami jednostkowymi. Wszystkie zmiany w kodzie muszą przejść tę kontrolę. Wszystkie skrypty testowe uruchamiane ręcznie są następnie automatyzowane.

Aby pomyślnie wdrożyć procesy biznesowe przez „Marcusa”, musieliśmy rozwiązać szereg problemów technologicznych, o każdym po kolei.

Zadanie 1. Potrzeba skalowalności poziomej systemu

Aby rozwiązać ten problem, wybraliśmy podejście do architektury oparte na mikrousługach. Jednocześnie bardzo ważne było zrozumienie obszarów odpowiedzialności służb. Staraliśmy się podzielić je na operacje biznesowe, biorąc pod uwagę specyfikę procesów. Przykładowo przyjęcie w magazynie nie jest operacją zbyt częstą, ale za to bardzo zakrojoną na szeroką skalę, podczas której konieczne jest szybkie uzyskanie od państwowego organu regulacyjnego informacji o przyjmowanych jednostkach towarowych, których ilość w jednej dostawie sięga 600000 tys. , sprawdź dopuszczalność przyjęcia tego produktu na magazyn i zwróć wszystkie niezbędne informacje dla systemu automatyzacji magazynu. Natomiast wysyłki z magazynów mają znacznie większą intensywność, ale jednocześnie operują małymi wolumenami danych.

Wszystkie usługi wdrażamy bezstanowo, a nawet staramy się dzielić operacje wewnętrzne na etapy, używając tego, co nazywamy tematami własnymi Kafki. Dzieje się tak, gdy mikrousługa wysyła komunikat do siebie, co pozwala zrównoważyć obciążenie operacjami wymagającymi większej ilości zasobów i upraszcza konserwację produktu, ale o tym później.

Postanowiliśmy wydzielić moduły interakcji z systemami zewnętrznymi na osobne usługi. Pozwoliło to rozwiązać problem często zmieniających się API systemów zewnętrznych, praktycznie bez wpływu na usługi o funkcjonalności biznesowej.

„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Wszystkie mikroserwisy wdrażane są w klastrze OpenShift, co rozwiązuje zarówno problem skalowania każdego mikroserwisu, jak i pozwala nam nie korzystać z zewnętrznych narzędzi Service Discovery.

Zadanie 2. Konieczność utrzymania dużego obciążenia i bardzo intensywnej wymiany danych pomiędzy usługami platformy: Tylko w fazie uruchamiania projektu wykonywanych jest około 600 operacji na sekundę. Oczekujemy, że wartość ta wzrośnie do 5000 operacji na sekundę w miarę łączenia się punktów sprzedaży detalicznej z naszą platformą.

Problem ten rozwiązano wdrażając klaster Kafki i niemal całkowicie rezygnując z synchronicznej interakcji pomiędzy mikroserwisami platformy. Wymaga to bardzo dokładnej analizy wymagań systemowych, ponieważ nie wszystkie operacje mogą być asynchroniczne. Jednocześnie za pośrednictwem brokera nie tylko przekazujemy zdarzenia, ale także przekazujemy w wiadomości wszystkie wymagane informacje biznesowe. Zatem rozmiar wiadomości może osiągnąć kilkaset kilobajtów. Limit wielkości wiadomości w Kafce wymaga od nas dokładnego przewidzenia wielkości wiadomości i w razie potrzeby dzielimy je, ale podział jest logiczny, związany z operacjami biznesowymi.
Na przykład towar, który przyjeżdża samochodem, dzielimy na pudła. W przypadku operacji synchronicznych przydzielane są osobne mikrousługi i przeprowadzane są dokładne testy obciążenia. Korzystanie z Kafki postawiło nas przed kolejnym wyzwaniem – przetestowanie działania naszej usługi z uwzględnieniem integracji z Kafką sprawia, że ​​wszystkie nasze testy jednostkowe są asynchroniczne. Rozwiązaliśmy ten problem, pisząc własne metody użytkowe przy użyciu Embedded Kafka Broker. Nie eliminuje to konieczności pisania testów jednostkowych dla poszczególnych metod, ale wolimy testować złożone przypadki za pomocą Kafki.

Dużo uwagi poświęcono śledzeniu logów, aby ich TraceId nie został utracony w przypadku wystąpienia wyjątków podczas działania usług lub podczas pracy z wsadem Kafki. A jeśli w pierwszym przypadku nie było żadnych specjalnych problemów, w drugim przypadku jesteśmy zmuszeni zarejestrować wszystkie identyfikatory TraceId, z którymi przyszła partia, i wybrać jeden, aby kontynuować śledzenie. Następnie, szukając według oryginalnego TraceId, użytkownik z łatwością dowie się, z jakiego powodu śledzenie było kontynuowane.

Zadanie 3. Konieczność przechowywania dużej ilości danych: Do X1 trafia rocznie ponad 5 miliard etykiet samych wyrobów tytoniowych. Wymagają stałego i szybkiego dostępu. W sumie system musi przetworzyć około 10 miliardów zapisów historii ruchu tych oznakowanych towarów.

Aby rozwiązać trzeci problem, wybrano bazę danych NoSQL MongoDB. Zbudowaliśmy fragment składający się z 5 węzłów, a każdy węzeł ma zestaw replik składający się z 3 serwerów. Pozwala to na skalowanie systemu w poziomie, dodawanie nowych serwerów do klastra i zapewnia jego odporność na awarie. Tutaj napotkaliśmy kolejny problem – zapewnienie transakcyjności w klastrze mongo, z uwzględnieniem wykorzystania mikroserwisów skalowalnych poziomo. Przykładowo jednym z zadań naszego systemu jest identyfikacja prób odsprzedaży produktów opatrzonych tymi samymi kodami etykiet. Tutaj pojawiają się nakładki z błędnymi skanami lub błędnymi operacjami kasjerów. Ustaliliśmy, że takie duplikaty mogą wystąpić zarówno w obrębie jednej przetwarzanej partii Kafki, jak i w obrębie dwóch partii przetwarzanych równolegle. Zatem sprawdzenie duplikatów poprzez zapytanie do bazy danych nic nie dało. Dla każdego mikroserwisu rozwiązaliśmy problem osobno w oparciu o logikę biznesową tej usługi. Na przykład w przypadku czeków dodaliśmy kontrolę wewnątrz partii i osobne przetwarzanie pod kątem występowania duplikatów podczas wstawiania.

Aby mieć pewność, że praca użytkowników z historią operacji nie wpływa w żaden sposób na to, co najważniejsze – na funkcjonowanie naszych procesów biznesowych, wszystkie dane historyczne wydzieliliśmy w oddzielną usługę z osobną bazą danych, która również otrzymuje informacje poprzez Kafkę . W ten sposób użytkownicy pracują z wyizolowaną usługą, nie wpływając na usługi przetwarzające dane w ramach bieżących operacji.

Zadanie 4: Ponowne przetwarzanie i monitorowanie kolejki:

W systemach rozproszonych nieuchronnie pojawiają się problemy i błędy związane z dostępnością baz danych, kolejek i zewnętrznych źródeł danych. W przypadku Marcusa źródłem takich błędów jest integracja z systemami zewnętrznymi. Należało znaleźć rozwiązanie, które pozwoliłoby na powtarzanie żądań błędnych odpowiedzi z określonym limitem czasu, ale jednocześnie nie wstrzymywałoby przetwarzania udanych żądań w kolejce głównej. W tym celu wybrano tzw. koncepcję „ponownej próby opartej na temacie”. Dla każdego tematu głównego tworzony jest jeden lub więcej tematów retry, do których wysyłane są błędne komunikaty, jednocześnie eliminowane jest opóźnienie w przetwarzaniu wiadomości z tematu głównego. Schemat interakcji -

„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Aby wdrożyć taki schemat, potrzebowaliśmy: zintegrować to rozwiązanie ze Springiem i uniknąć duplikacji kodu. Surfując po sieci natrafiliśmy na podobne rozwiązanie bazujące na Spring BeanPostProccessor, jednak wydało nam się to niepotrzebnie uciążliwe. Nasz zespół stworzył prostsze rozwiązanie, które pozwala nam zintegrować się z cyklem wiosennym w celu tworzenia konsumentów i dodatkowo dodać Retry Consumers. Prototyp naszego rozwiązania zaproponowaliśmy zespołowi Springa, możecie to zobaczyć tutaj. Liczbę konsumentów ponownych prób i liczbę prób dla każdego konsumenta konfiguruje się za pomocą parametrów, w zależności od potrzeb procesu biznesowego, a aby wszystko zadziałało, wystarczy dodać adnotację org.springframework.kafka.annotation.KafkaListener , który jest znany wszystkim programistom Springa.

Jeśli wiadomość nie mogła zostać przetworzona po wszystkich ponownych próbach, trafia do DLT (temat niedostarczonej wiadomości) przy użyciu narzędzia Spring DeadLetterPublishingRecoverer. Na prośbę wsparcia rozszerzyliśmy tę funkcjonalność i stworzyliśmy osobną usługę umożliwiającą przeglądanie wiadomości zawartych w DLT, stackTrace, traciId i innych przydatnych informacji na ich temat. Dodatkowo do wszystkich tematów DLT dodano monitorowanie i alerty i teraz tak naprawdę pojawienie się komunikatu w temacie DLT jest powodem do przeanalizowania i naprawienia usterki. Jest to bardzo wygodne – po nazwie tematu od razu rozumiemy, na jakim etapie procesu pojawił się problem, co znacznie przyspiesza poszukiwanie jego pierwotnej przyczyny.

„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Ostatnio wdrożyliśmy interfejs, który pozwala na ponowne przesłanie wiadomości za pomocą naszego wsparcia po wyeliminowaniu ich przyczyn (np. przywróceniu funkcjonalności systemu zewnętrznego) i oczywiście ustaleniu odpowiedniej usterki do analizy. W tym miejscu przydadzą się nasze tematy własne: aby nie rozpoczynać od nowa długiego łańcucha przetwarzania, możesz go zrestartować od żądanego kroku.

„Wchodzę w moje buty” – czekaj, czy są oznaczone?

Obsługa Platformy

Platforma już działa produktywnie, codziennie realizujemy dostawy i wysyłki, podłączamy nowe centra dystrybucyjne i sklepy. W ramach pilotażu system współpracuje z grupami produktowymi „Tytoń” i „Obuwie”.

Cały nasz zespół bierze udział w przeprowadzaniu pilotaży, analizuje pojawiające się problemy i zgłasza sugestie dotyczące ulepszenia naszego produktu, od ulepszania logów po zmianę procesów.

Aby nie powtarzać naszych błędów, wszystkie przypadki wykryte podczas pilotażu znajdują odzwierciedlenie w testach automatycznych. Obecność dużej liczby autotestów i testów jednostkowych pozwala przeprowadzić testy regresyjne i zainstalować poprawkę dosłownie w ciągu kilku godzin.

Teraz wciąż rozwijamy i udoskonalamy naszą platformę i ciągle stawiamy czoła nowym wyzwaniom. Jeśli jesteś zainteresowany, o naszych rozwiązaniach porozmawiamy w kolejnych artykułach.

Źródło: www.habr.com

Dodaj komentarz