Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Co mogłoby zmusić tak dużą firmę jak Lamoda, posiadającą usprawniony proces i dziesiątki wzajemnie powiązanych usług, do istotnej zmiany swojego podejścia? Motywacja może być zupełnie inna: od legislacyjnej po chęć eksperymentowania właściwą każdemu programiście.

Nie oznacza to jednak, że nie można liczyć na dodatkowe korzyści. Sergey Zaika opowie Ci, co dokładnie możesz zyskać, wdrażając API sterowane zdarzeniami na platformie Kafka (kilkaalda). Na pewno nie zabraknie też mowy o wielkich strzałach i ciekawych odkryciach – bez nich eksperyment nie może się obejść.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Zastrzeżenie: ten artykuł powstał w oparciu o materiały ze spotkania, które Sergey zorganizował w listopadzie 2018 r. w programie HighLoad++. Doświadczenia Lamody na żywo związane ze współpracą z Kafką przyciągnęły słuchaczy nie mniej niż inne reportaże zawarte w programie. Uważamy, że jest to doskonały przykład na to, że zawsze można i należy znaleźć ludzi podobnie myślących, a organizatorzy HighLoad++ w dalszym ciągu będą się starali stworzyć sprzyjającą temu atmosferę.

O procesie

Lamoda to duża platforma e-commerce, która posiada własne contact center, firmę kurierską (i wiele oddziałów), studio fotograficzne, ogromny magazyn, a wszystko to działa na własnym oprogramowaniu. Istnieją dziesiątki metod płatności, partnerów B2B, którzy mogą korzystać z niektórych lub wszystkich tych usług i chcą znać aktualne informacje o swoich produktach. Poza tym Lamoda działa w trzech krajach poza Federacją Rosyjską i tam wszystko jest trochę inne. W sumie sposobów na skonfigurowanie nowego zamówienia jest prawdopodobnie ponad sto, które należy zrealizować na swój sposób. Wszystko to działa za pomocą dziesiątek usług, które czasami komunikują się w nieoczywisty sposób. Istnieje również system centralny, którego główną odpowiedzialnością są statusy zamówień. Nazywamy ją BOB, pracuję z nią.

Narzędzie do zwrotu pieniędzy z API sterowanym zdarzeniami

Słowo sterowane zdarzeniami jest dość oklepane; nieco dalej zdefiniujemy bardziej szczegółowo, co przez to rozumie. Zacznę od kontekstu, w którym zdecydowaliśmy się wypróbować podejście API sterowane zdarzeniami w Kafce.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

W każdym sklepie oprócz zamówień, za które klienci płacą, zdarzają się sytuacje, w których sklep ma obowiązek zwrócić pieniądze, ponieważ produkt nie przypadł klientowi do gustu. Jest to stosunkowo krótki proces: w razie potrzeby wyjaśniamy informacje i przekazujemy pieniądze.

Jednak zwrot stał się bardziej skomplikowany ze względu na zmiany w przepisach i musieliśmy wdrożyć dla niego osobną mikrousługę.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Nasza motywacja:

  1. Prawo FZ-54 - w skrócie, prawo wymaga zgłaszania do urzędu skarbowego każdej transakcji pieniężnej, czy to zeznania, czy pokwitowania, w ciągu dość krótkiej, kilkuminutowej umowy SLA. Jako firma e-commerce wykonujemy całkiem sporo operacji. Z technicznego punktu widzenia oznacza to nową odpowiedzialność (a tym samym nową usługę) i ulepszenia wszystkich zaangażowanych systemów.
  2. Rozłam BOB-a to wewnętrzny projekt firmy, mający na celu odciążenie BOB z dużej liczby obowiązków pobocznych i zmniejszenie jego ogólnej złożoności.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Ten schemat przedstawia główne systemy Lamoda. Teraz większość z nich jest większa konstelacja 5–10 mikrousług wokół kurczącego się monolitu. Powoli rosną, ale staramy się je zmniejszać, bo rozłożenie wybranego w środku fragmentu jest przerażające - nie możemy pozwolić, żeby spadł. Jesteśmy zmuszeni zarezerwować wszystkie wymiany (strzałki) i liczyć się z tym, że któraś z nich może okazać się niedostępna.

BOB ma również całkiem sporo giełd: systemy płatności, systemy dostaw, systemy powiadomień itp.

Technicznie BOB to:

  • ~150 tys. linii kodu + ~100 tys. linii testów;
  • php7.2 + Zend 1 i komponenty Symfony 3;
  • >100 interfejsów API i ~50 integracji wychodzących;
  • 4 kraje z własną logiką biznesową.

Wdrożenie BOB-a jest drogie i bolesne, ilość kodu i problemów, które rozwiązuje jest taka, że ​​nikt nie jest w stanie tego wszystkiego sobie wyobrazić. Ogólnie rzecz biorąc, istnieje wiele powodów, aby to uprościć.

Proces zwrotu

Początkowo w procesie biorą udział dwa systemy: BOB i Płatność. Teraz pojawiają się dwa kolejne:

  • Służba Fiskalizacji, która zajmie się problemami związanymi z fiskalizacją i komunikacją ze służbami zewnętrznymi.
  • Narzędzie do zwrotu pieniędzy, które po prostu zawiera nowe wymiany, aby nie zawyżać BOB.

Teraz proces wygląda następująco:

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

  1. BOB otrzymuje prośbę o zwrot pieniędzy.
  2. BOB mówi o tym narzędziu zwrotu pieniędzy.
  3. Narzędzie zwrotu pieniędzy informuje płatność: „Zwróć pieniądze”.
  4. Płatność zwraca pieniądze.
  5. Refund Tool i BOB synchronizują ze sobą statusy, bo na razie obydwoje tego potrzebują. Nie jesteśmy jeszcze gotowi, aby całkowicie przejść na narzędzie do zwrotu pieniędzy, ponieważ BOB ma interfejs użytkownika, raporty do księgowości i ogólnie dużo danych, których nie można tak łatwo przenieść. Musisz usiąść na dwóch krzesłach.
  6. Żądanie fiskalizacji odpada.

W efekcie stworzyliśmy na Kafce swego rodzaju autobus eventowy – event-bus, od którego wszystko się zaczęło. Hurra, teraz mamy pojedynczy punkt awarii (sarkazm).

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Plusy i minusy są dość oczywiste. Zbudowaliśmy autobus, co oznacza, że ​​​​teraz wszystkie usługi są od niego zależne. Upraszcza to projekt, ale wprowadza do systemu pojedynczy punkt awarii. Kafka ulegnie awarii, proces się zatrzyma.

Co to jest interfejs API sterowany zdarzeniami

Dobrą odpowiedź na to pytanie można znaleźć w raporcie Martina Fowlera (GOTO 2017) „Wiele znaczeń architektury sterowanej zdarzeniami”.

W skrócie co zrobiliśmy:

  1. Zakończ wszystkie wymiany asynchroniczne za pośrednictwem przechowywanie wydarzeń. Zamiast informować każdego zainteresowanego konsumenta o zmianie statusu w sieci, piszemy wydarzenie o zmianie statusu na scentralizowany magazyn, a konsumenci zainteresowani tematem czytają wszystko, co się stamtąd pojawi.
  2. Zdarzeniem w tym przypadku jest powiadomienie (Powiadomienia), że coś się gdzieś zmieniło. Na przykład zmienił się status zamówienia. Konsument zainteresowany niektórymi danymi towarzyszącymi zmianie statusu, które nie są zawarte w zgłoszeniu, może sam dowiedzieć się o ich statusie.
  3. Opcja maksymalna to pełnoprawne pozyskiwanie wydarzeń, przeniesienie stanu, w którym to zdarzeniu znajdują się wszystkie informacje niezbędne do przetwarzania: skąd pochodzi i jaki status osiągnął, jak dokładnie dane się zmieniły itp. Pytanie tylko dotyczy wykonalności i ilości informacji, które możesz sobie pozwolić na przechowywanie.

W ramach uruchomienia Narzędzia Zwrotu skorzystaliśmy z trzeciej możliwości. To uproszczone przetwarzanie zdarzeń, ponieważ nie było potrzeby wyodrębniania szczegółowych informacji, a także wyeliminowało scenariusz, w którym każde nowe zdarzenie generuje serię wyjaśniających żądań uzyskania od konsumentów.

Usługa narzędzia do zwrotu pieniędzy nie załadowano, więc u Kafki bardziej chodzi o gust pióra niż o konieczność. Nie sądzę, że gdyby usługa zwrotu pieniędzy stała się projektem obciążającym duże obciążenia, biznes byłby zadowolony.

Wymiana asynchroniczna TAK JAK JEST

W przypadku wymian asynchronicznych dział PHP zwykle korzysta z RabbitMQ. Zebraliśmy dane do żądania, umieściliśmy je w kolejce, a konsument tej samej usługi przeczytał je i wysłał (lub nie wysłał). W przypadku samego interfejsu API Lamoda aktywnie korzysta ze Swaggera. Projektujemy API, opisujemy je w Swaggerze oraz generujemy kod klienta i serwera. Używamy również nieco ulepszonego JSON RPC 2.0.

W niektórych miejscach używane są magistrale ESB, w innych na activeMQ, ale ogólnie RabbitMQ - standard.

Wymiana asynchroniczna TO BE

Projektując wymianę poprzez magistralę zdarzeń, można doszukać się analogii. Podobnie opisujemy przyszłą wymianę danych poprzez opisy struktury zdarzeń. Format yaml, musieliśmy sami wygenerować kod, generator tworzy DTO zgodnie ze specyfikacją i uczy klientów i serwery pracy z nimi. Pokolenie idzie w dwa języki - golanga i php. Pomaga to zachować spójność bibliotek. Generator jest napisany w języku golang i dlatego otrzymał nazwę gogi.

Event-sourcing na platformie Kafka jest rzeczą typową. Istnieje rozwiązanie z głównej wersji korporacyjnej Kafka Confluent nakadi, rozwiązanie od naszych braci domenowych Zalando. Nasz motywacja do rozpoczęcia pracy z waniliową Kafką - oznacza to pozostawienie rozwiązania do czasu, aż ostatecznie zdecydujemy, czy będziemy z niego korzystać wszędzie, ale także pozostawienie sobie pola manewru i ulepszeń: chcemy wsparcia dla naszych JSON RPC 2.0, generatory dla dwóch języków i zobaczmy co jeszcze.

Ironią losu jest to, że nawet w tak szczęśliwym przypadku, gdy istnieje z grubsza podobny biznes, Zalando, który stworzył z grubsza podobne rozwiązanie, nie potrafimy z niego skutecznie skorzystać.

Schemat architektoniczny w momencie uruchomienia jest następujący: czytamy bezpośrednio z Kafki, ale piszemy tylko poprzez magistralę zdarzeń. W Kafce jest wiele do przeczytania: brokerzy, balansery i jest to mniej więcej gotowe do skalowania poziomego, chciałem to zachować. Chcieliśmy dokończyć nagrywanie za pośrednictwem jednej bramki, czyli magistrali zdarzeń, i oto dlaczego.

Wydarzenia-autobus

Albo autobus eventowy. Jest to po prostu bezstanowa brama http, która pełni kilka ważnych ról:

  • Walidacja produkcji — sprawdzamy, czy wydarzenia odpowiadają naszym specyfikacjom.
  • Główny system wydarzeń, czyli jest to główny i jedyny system w firmie, który odpowiada na pytanie, przy jakich zdarzeniach, które konstrukcje są uznawane za ważne. Walidacja obejmuje po prostu typy danych i wyliczenia w celu ścisłego określenia treści.
  • Funkcja mieszająca w przypadku fragmentowania - struktura wiadomości Kafki to klucz-wartość i przy użyciu skrótu klucza obliczane jest, gdzie go umieścić.

Czemu

Pracujemy w dużej firmie z usprawnionym procesem. Po co cokolwiek zmieniać? To jest eksperymenti spodziewamy się uzyskać szereg korzyści.

Wymiany 1:n+1 (jeden do wielu)

Kafka bardzo ułatwia łączenie nowych konsumentów z API.

Załóżmy, że masz katalog, który musisz aktualizować w kilku systemach jednocześnie (i w niektórych nowych). Wcześniej wymyśliliśmy pakiet, który implementował set-API, a system główny był informowany o adresach konsumentów. Teraz system główny wysyła aktualizacje tematu i każdy zainteresowany go czyta. Pojawił się nowy system - zapisaliśmy go dla tematu. Tak, również w pakiecie, ale prościej.

W przypadku narzędzia do zwrotu pieniędzy, które jest kawałkiem BOB, wygodnie jest dla nas synchronizować je poprzez Kafkę. Płatność mówi, że pieniądze zostały zwrócone: BOB, RT dowiedziały się o tym, zmieniły swoje statusy, Służba Fiskalizacyjna dowiedziała się o tym i wystawiła czek.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

W planach mamy stworzenie ujednoliconej usługi powiadomień, która powiadamiałaby klienta o nowościach dotyczących jego zamówienia/zwrotów. Teraz ta odpowiedzialność jest rozłożona pomiędzy systemami. Wystarczy, że nauczymy Usługę Powiadomień wychwytywania odpowiednich informacji z Kafki i reagowania na nie (oraz wyłączania tych powiadomień w innych systemach). Nie będą wymagane żadne nowe bezpośrednie wymiany.

Oparte na danych

Informacje pomiędzy systemami stają się przejrzyste – niezależnie od tego, jakie „cholerne przedsięwzięcie” prowadzisz i bez względu na to, jak duże są Twoje zaległości. Lamoda posiada dział Data Analytics, który zbiera dane z systemów i umieszcza je w formie umożliwiającej ponowne wykorzystanie, zarówno dla biznesu, jak i dla systemów inteligentnych. Kafka pozwala szybko przekazać im dużą ilość danych i zapewnić ich aktualność.

Dziennik replikacji

Wiadomości nie znikają po przeczytaniu, jak w RabbitMQ. Gdy zdarzenie zawiera wystarczającą ilość informacji do przetworzenia, mamy historię ostatnich zmian w obiekcie i, w razie potrzeby, możliwość zastosowania tych zmian.

Okres przechowywania dziennika replikacji zależny jest od intensywności pisania w tym temacie; Kafka pozwala elastycznie ustawić limity czasu przechowywania i objętości danych. W przypadku intensywnych tematów ważne jest, aby wszyscy konsumenci mieli czas na przeczytanie informacji, zanim znikną, nawet w przypadku krótkotrwałej niesprawności. Zwykle możliwe jest przechowywanie danych jednostki dni, co w zupełności wystarczy do wsparcia.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Następnie małe powtórzenie dokumentacji dla tych, którzy nie znają Kafki (zdjęcie również pochodzi z dokumentacji)

AMQP ma kolejki: piszemy wiadomości do kolejki dla konsumenta. Zwykle jedna kolejka jest przetwarzana przez jeden system z tą samą logiką biznesową. Jeśli chcesz powiadomić kilka systemów, możesz nauczyć aplikację zapisywania do kilku kolejek lub skonfigurować wymianę z mechanizmem fanout, który sam je klonuje.

Kafka ma podobną abstrakcję aktualny, w którym piszesz wiadomości, ale nie znikają one po przeczytaniu. Domyślnie po połączeniu się z Kafką otrzymujesz wszystkie wiadomości i masz możliwość zapisania miejsca, w którym je przerwałeś. Oznacza to, że czytasz sekwencyjnie, nie możesz oznaczyć wiadomości jako przeczytanej, ale zapisz identyfikator, z którego będziesz mógł kontynuować czytanie. Identyfikator, który ustaliłeś, nazywa się przesunięciem, a mechanizm jest przesunięciem zatwierdzania.

W związku z tym można zastosować inną logikę. Np. BOB mamy w 4 instancjach dla różnych krajów - Lamoda jest w Rosji, Kazachstanie, Ukrainie, Białorusi. Ponieważ są wdrażane osobno, mają nieco inne konfiguracje i własną logikę biznesową. W wiadomości wskazujemy, jakiego kraju dotyczy. Każdy konsument BOB w każdym kraju czyta z innym groupId, a jeśli komunikat go nie dotyczy, to go pomija, tj. natychmiast zatwierdza offset +1. Jeśli ten sam temat czyta nasz Serwis Płatniczy, to robi to odrębną grupą, w związku z czym offsety się nie krzyżują.

Wymagania dotyczące wydarzenia:

  • Kompletność danych. Zależy mi na tym, aby zdarzenie miało wystarczającą ilość danych, aby można było je przetworzyć.

  • Uczciwość Do Events-bus delegujemy weryfikację, czy zdarzenie jest spójne i może je przetworzyć.
  • Kolejność jest ważna. W przypadku powrotu zmuszeni jesteśmy pracować z historią. W przypadku powiadomień kolejność nie ma znaczenia, jeśli są to powiadomienia jednorodne, e-mail będzie taki sam niezależnie od tego, które zamówienie dotarło jako pierwsze. W przypadku zwrotu środków proces jest przejrzysty, jeśli zmienimy zamówienie, pojawią się wyjątki, zwrot nie zostanie utworzony ani zrealizowany – skończymy w innym statusie.
  • Spójność. Mamy sklep, a teraz zamiast API tworzymy wydarzenia. Potrzebujemy sposobu na szybkie i tanie przesyłanie do naszych serwisów informacji o nowych wydarzeniach i zmianach w istniejących. Osiąga się to poprzez wspólną specyfikację w oddzielnym repozytorium git i generatorach kodu. Dlatego klienci i serwery w różnych usługach są skoordynowane.

Kafka w Lamodzie

Mamy trzy instalacje Kafki:

  1. dzienniki;
  2. R & D;
  3. Wydarzenia-autobus.

Dziś mówimy tylko o ostatnim punkcie. W event-bus nie mamy zbyt dużych instalacji - 3 brokerów (serwery) i tylko 27 tematów. Z reguły jeden temat to jeden proces. Ale to jest subtelna kwestia i teraz się nią zajmiemy.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Powyżej znajduje się wykres obrotów. Proces zwrotu środków oznaczony jest turkusową linią (tak, tą na osi X), a różowa linia to proces aktualizacji treści.

Katalog Lamoda zawiera miliony produktów, a dane są na bieżąco aktualizowane. Niektóre kolekcje wychodzą z mody, na ich miejsce wydawane są nowe, a w katalogu ciągle pojawiają się nowe modele. Staramy się przewidzieć, co jutro będzie interesujące dla naszych klientów, dlatego stale kupujemy nowe rzeczy, fotografujemy je i aktualizujemy gablotę.

Różowe szczyty to aktualizacje produktów, czyli zmiany w produktach. Widać, że chłopaki robili zdjęcia, robili zdjęcia i jeszcze raz! — załadował pakiet wydarzeń.

Przypadki użycia Lamoda Events

Zbudowaną architekturę wykorzystujemy do następujących operacji:

  • Śledzenie statusu zwrotu: wezwanie do działania i śledzenie statusu ze wszystkich zaangażowanych systemów. Płatność, statusy, fiskalizacja, powiadomienia. Tutaj przetestowaliśmy to podejście, stworzyliśmy narzędzia, zebraliśmy wszystkie błędy, napisaliśmy dokumentację i powiedzieliśmy naszym kolegom, jak z niej korzystać.
  • Aktualizacja kart produktów: konfiguracja, metadane, charakterystyka. Jeden system czyta (który wyświetla), a kilka zapisuje.
  • E-mail, push i sms: zamówienie zostało odebrane, zamówienie dotarło, zwrot został przyjęty itp., jest ich dużo.
  • Zapasy, odnowienie magazynu — aktualizacja ilościowa towarów, same liczby: przybycie na magazyn, zwrot. Konieczne jest, aby wszystkie systemy związane z rezerwacją towarów działały na najbardziej aktualnych danych. Obecnie system aktualizacji zapasów jest dość skomplikowany, Kafka go uprości.
  • Analiza danych (dział R&D), narzędzia ML, analityka, statystyki. Zależy nam na przejrzystości informacji – Kafka świetnie się do tego nadaje.

Teraz bardziej interesująca część dotycząca wielkich wstrząsów i interesujących odkryć, które miały miejsce w ciągu ostatnich sześciu miesięcy.

Problemy projektowe

Załóżmy, że chcemy zrobić nową rzecz - na przykład przenieść cały proces dostawy do Kafki. Teraz część procesu jest realizowana w Przetwarzaniu Zamówień w BOB. Za przeniesieniem zamówienia do usługi dostawy, przeniesieniem do magazynu pośredniego i tak dalej kryje się model statusu. Istnieje cały monolit, a nawet dwa, plus kilka API dedykowanych do dostarczania. Wiedzą znacznie więcej o dostawie.

Z pozoru są to podobne obszary, jednak Realizacja Zamówienia w BOB i Systemie Wysyłek mają różne statusy. Przykładowo niektóre firmy kurierskie nie wysyłają statusów pośrednich, a jedynie końcowe: „dostarczono” lub „zaginęło”. Inni natomiast bardzo szczegółowo informują o przepływie towarów. Każdy ma swoje zasady walidacji: dla niektórych e-mail jest ważny, co oznacza, że ​​zostanie przetworzony; dla innych jest to nieważne, ale zamówienie i tak zostanie zrealizowane, bo jest numer telefonu do kontaktu, a ktoś powie, że takie zamówienie w ogóle nie zostanie zrealizowane.

Strumień danych

W przypadku Kafki pojawia się pytanie o organizację przepływu danych. Zadanie to polega na wybraniu strategii opartej na kilku punktach; przejrzyjmy je wszystkie.

W jednym temacie czy w różnych?

Mamy specyfikację wydarzenia. W BOB piszemy, że ma zostać dostarczone takie a takie zamówienie i wskazujemy: numer zamówienia, jego skład, niektóre SKU i kody kreskowe itp. Gdy towar dotrze do magazynu, dostawa będzie mogła otrzymać statusy, znaczniki czasu i wszystko, co jest potrzebne. Ale wtedy chcemy otrzymywać aktualizacje tych danych w BOB. Mamy odwrotny proces otrzymywania danych z dostawy. Czy to to samo wydarzenie? A może jest to osobna wymiana zdań, zasługująca na własny temat?

Najprawdopodobniej będą one bardzo podobne i pokusa założenia jednego tematu nie jest bezpodstawna, bo osobny temat to oddzielni konsumenci, osobne konfiguracje, osobna generacja tego wszystkiego. Ale nie fakt.

Nowe pole czy nowe wydarzenie?

Ale jeśli użyjesz tych samych wydarzeń, pojawi się inny problem. Na przykład nie wszystkie systemy dostarczania mogą generować rodzaj DTO, jaki może wygenerować BOB. Wysyłamy im identyfikator, ale oni go nie zapisują, bo nie jest im to potrzebne, a z punktu widzenia uruchomienia procesu event-bus to pole jest wymagane.

Jeśli wprowadzimy regułę dla magistrali zdarzeń, że to pole jest wymagane, wówczas będziemy zmuszeni ustawić dodatkowe reguły walidacji w BOB lub w procedurze obsługi zdarzenia startowego. Walidacja zaczyna rozprzestrzeniać się w całej usłudze - nie jest to zbyt wygodne.

Kolejnym problemem jest pokusa stopniowego rozwoju. Mówi się nam, że trzeba coś dodać do tego wydarzenia i może, jeśli się nad tym zastanowić, powinno to być osobne wydarzenie. Ale w naszym schemacie osobne wydarzenie to osobny temat. Osobnym tematem jest cały proces, który opisałem powyżej. Programistę kusi, aby po prostu dodać kolejne pole do schematu JSON i je zregenerować.

W przypadku zwrotów na wydarzenie dotarliśmy w pół roku. Mieliśmy jedno meta-zdarzenie zwane aktualizacją zwrotu pieniędzy, które zawierało pole typu opisujące, czym właściwie była ta aktualizacja. Z tego powodu mieliśmy „cudowne” przełączniki z walidatorami, którzy powiedzieli nam, jak zweryfikować to zdarzenie za pomocą tego typu.

Wersjonowanie zdarzeń

Aby sprawdzić poprawność wiadomości w Kafce, możesz użyć euro, ale trzeba było od razu się na nim położyć i skorzystać z Confluentu. W naszym przypadku musimy zachować ostrożność przy wersjonowaniu. Nie zawsze będzie możliwe ponowne odczytanie komunikatów z dziennika replikacji, ponieważ model „wyszedł”. Zasadniczo okazuje się, że buduje się wersje tak, aby model był kompatybilny wstecz: na przykład należy tymczasowo ustawić pole jako opcjonalne. Jeśli różnice są zbyt duże, zaczynamy pisać w nowym temacie, a klientów przenosimy, gdy skończą czytać stary.

Gwarantowana kolejność odczytu partycji

Tematy wewnątrz Kafki są podzielone na partycje. Nie jest to bardzo ważne, gdy projektujemy podmioty i giełdy, ale jest ważne przy podejmowaniu decyzji, jak je skonsumować i skalować.

W zwykłym przypadku piszesz jeden temat w Kafce. Domyślnie używana jest jedna partycja i do niej trafiają wszystkie wiadomości z tego tematu. Konsument w konsekwencji czyta te wiadomości sekwencyjnie. Załóżmy, że musimy teraz rozbudować system tak, aby wiadomości czytało dwóch różnych konsumentów. Jeśli na przykład wysyłasz SMS-y, możesz powiedzieć Kafce, aby utworzył dodatkową partycję, a Kafka zacznie dzielić wiadomości na dwie części - połowę tutaj, połowę tutaj.

Jak Kafka je dzieli? Każda wiadomość ma treść (w której przechowujemy JSON) i klucz. Do tego klucza możesz dołączyć funkcję skrótu, która określi, do której partycji trafi wiadomość.

W naszym przypadku ze zwrotami ma to znaczenie, jeśli weźmiemy dwie partycje, to jest szansa, że ​​równoległy odbiorca przetworzy drugie zdarzenie przed pierwszym i będą kłopoty. Funkcja skrótu zapewnia, że ​​wiadomości o tym samym kluczu trafiają do tej samej partycji.

Zdarzenia vs polecenia

To kolejny problem, z którym się spotkaliśmy. Zdarzenie to określone wydarzenie: mówimy, że coś się gdzieś wydarzyło (coś się wydarzyło), na przykład przedmiot został anulowany lub nastąpił zwrot pieniędzy. Jeśli ktoś odsłucha te zdarzenia, to zgodnie z „przedmiotem anulowanym” zostanie utworzona jednostka zwrotu, a „zwrot nastąpił” zostanie zapisany gdzieś w ustawieniach.

Ale zazwyczaj projektując wydarzenia, nie chcesz ich pisać na próżno - liczysz na to, że ktoś je przeczyta. Istnieje duża pokusa, aby napisać, że nie coś się_zdarzyło się (przedmiot_anulowany, zwrot_zwrócono), ale coś, co_należy_zrobić. Na przykład przedmiot jest gotowy do zwrotu.

Z jednej strony sugeruje, w jaki sposób wydarzenie zostanie wykorzystane. Z drugiej strony brzmi znacznie mniej jak zwykła nazwa wydarzenia. Poza tym niedaleko stąd do komendy do_coś. Ale nie masz gwarancji, że ktoś przeczyta to wydarzenie; a jeśli to przeczytasz, to przeczytasz to pomyślnie; i jeśli przeczytałeś to pomyślnie, to coś zrobiłeś i to coś się udało. W momencie, gdy wydarzenie staje się czymś, konieczna staje się informacja zwrotna i to jest problem.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

W wymianie asynchronicznej w RabbitMQ po przeczytaniu wiadomości przejdź do http, masz odpowiedź - przynajmniej że wiadomość została odebrana. Kiedy piszesz do Kafki, pojawia się wiadomość, którą napisałeś do Kafki, ale nie wiesz nic o tym, jak została przetworzona.

Dlatego w naszym przypadku musieliśmy wprowadzić zdarzenie odpowiedzi i skonfigurować monitoring tak, aby przy wysłaniu takiej liczby zdarzeń po takim a takim czasie nadeszła taka sama liczba zdarzeń odpowiedzi. Jeśli tak się nie stanie, oznacza to, że coś poszło nie tak. Przykładowo, jeśli wysłaliśmy zdarzenie „item_ready_to_refund”, spodziewamy się, że zostanie utworzony zwrot pieniędzy, pieniądze zostaną zwrócone klientowi, a zdarzenie „money_refunded” zostanie wysłane do nas. Nie jest to jednak pewne, dlatego potrzebny jest monitoring.

Niuanse

Problem jest dość oczywisty: jeśli czytasz temat po kolei i masz jakiś zły przekaz, to konsument upadnie, a Ty nie pójdziesz dalej. Potrzebujesz zatrzymać wszystkich konsumentów, zatwierdź dalsze przesunięcie, aby kontynuować czytanie.

Wiedzieliśmy o tym, liczyliśmy na to, a jednak tak się stało. A stało się tak, ponieważ zdarzenie było ważne z punktu widzenia magistrali zdarzeń, zdarzenie było ważne z punktu widzenia walidatora aplikacji, ale nie było ważne z punktu widzenia PostgreSQL, ponieważ w naszym jednym systemie MySQL z UNSIGNED INT, a w nowo napisanym systemie był PostgreSQL tylko z INT. Jego rozmiar jest trochę mniejszy, a identyfikator nie pasował. Symfony umarło z wyjątkiem. My oczywiście złapaliśmy wyjątek, bo na nim polegaliśmy i mieliśmy zamiar zatwierdzić to przesunięcie, ale wcześniej chcieliśmy zwiększyć licznik problemów, ponieważ wiadomość została przetworzona bez powodzenia. Liczniki w tym projekcie są również w bazie danych, a Symfony zamknął już komunikację z bazą danych, a drugi wyjątek zabił cały proces bez szansy na zatwierdzenie przesunięcia.

Usługa odłożyła się na jakiś czas - na szczęście z Kafką nie jest tak źle, bo komunikaty pozostają. Po przywróceniu pracy możesz dokończyć ich czytanie. To jest wygodne.

Kafka ma możliwość ustawienia dowolnego przesunięcia za pomocą narzędzi. Ale aby to zrobić, musisz zatrzymać wszystkich konsumentów - w naszym przypadku przygotuj osobne wydanie, w którym nie będzie żadnych konsumentów, przesunięć. Następnie w Kafce możesz przesunąć przesunięcie za pomocą narzędzi, a wiadomość zostanie przekazana.

Kolejny niuans - dziennik replikacji vs rdkafka.so - jest związane ze specyfiką naszego projektu. Używamy PHP, a w PHP z reguły wszystkie biblioteki komunikują się z Kafką poprzez repozytorium rdkafka.so i wtedy pojawia się jakiś wrapper. Być może są to nasze osobiste trudności, ale okazało się, że samo ponowne przeczytanie fragmentu tego, co już przeczytaliśmy, nie jest takie proste. Ogólnie rzecz biorąc, wystąpiły problemy z oprogramowaniem.

Wracając do specyfiki pracy z partycjami, jest to napisane bezpośrednio w dokumentacji konsumenci >= partycje tematyczne. Ale dowiedziałem się o tym znacznie później, niż bym chciał. Jeśli chcesz skalować i mieć dwóch konsumentów, potrzebujesz co najmniej dwóch partycji. Oznacza to, że jeśli miałeś jedną partycję, w której zgromadziło się 20 tysięcy wiadomości, i utworzyłeś nową, liczba wiadomości nie zostanie wkrótce wyrównana. Dlatego, aby mieć dwóch równoległych konsumentów, musisz poradzić sobie z partycjami.

Monitorowanie

Myślę, że sposób, w jaki to monitorujemy, będzie jeszcze wyraźniejszy, jakie problemy występują w obecnym podejściu.

Przykładowo wyliczamy, ile produktów w bazie zmieniło ostatnio swój status i w związku z tym na podstawie tych zmian powinny nastąpić zdarzenia, i przesyłamy tę liczbę do naszego systemu monitoringu. Następnie od Kafki dostajemy drugą liczbę, ile faktycznie zdarzeń zostało zarejestrowanych. Oczywiście różnica między tymi dwiema liczbami powinna zawsze wynosić zero.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Ponadto należy monitorować, jak radzi sobie producent, czy magistrala zdarzeń odebrała komunikaty i jak radzi sobie konsument. Przykładowo na poniższych wykresach narzędzie Refund Tool radzi sobie dobrze, ale BOB wyraźnie ma pewne problemy (niebieskie wartości szczytowe).

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Wspomniałem już o opóźnieniu grupy konsumentów. Z grubsza jest to liczba nieprzeczytanych wiadomości. Ogólnie rzecz biorąc, nasi konsumenci pracują szybko, więc opóźnienie wynosi zwykle 0, ale czasami może wystąpić krótkotrwały szczyt. Kafka może to zrobić od razu po wyjęciu z pudełka, ale musisz ustawić określony interwał.

Jest projekt Noraco da ci więcej informacji na temat Kafki. Po prostu używa interfejsu API grupy konsumentów, aby określić stan działania tej grupy. Oprócz OK i Failed pojawia się ostrzeżenie i możesz dowiedzieć się, że Twoi konsumenci nie są w stanie wytrzymać tempa produkcji - nie mają czasu na korektę tego, co jest napisane. System jest dość inteligentny i łatwy w obsłudze.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Tak wygląda odpowiedź API. Oto grupa bob-live-fifa, partycja refund.update.v1, status OK, lag 0 - ostatnie ostateczne przesunięcie takie i takie.

Doświadczenie w rozwijaniu usługi Refund Tool z asynchronicznym API na platformie Kafka

Monitorowanie zaktualizowano_w SLA (zablokowano) Już wspomniałem. Przykładowo produkt zmienił status na gotowy do zwrotu. Instalujemy Crona, który mówi, że jeśli za 5 minut ten obiekt nie poszedł do zwrotu (bardzo szybko zwracamy pieniądze poprzez systemy płatności), to coś na pewno poszło nie tak i to zdecydowanie jest powód do wsparcia. Dlatego po prostu bierzemy Crona, który czyta takie rzeczy i jeśli są one większe od 0, to wysyła alert.

Podsumowując, używanie zdarzeń jest wygodne, gdy:

  • informacja jest potrzebna kilku systemom;
  • wynik przetwarzania nie jest ważny;
  • jest niewiele wydarzeń lub małych wydarzeń.

Wydawać by się mogło, że artykuł ma bardzo specyficzny temat – asynchroniczne API na Kafce, jednak w związku z tym chciałbym polecić wiele rzeczy na raz.
Najpierw, dalej Wysokie obciążenie++ trzeba poczekać do listopada, w kwietniu będzie wersja petersburska, a w czerwcu porozmawiamy o dużych obciążeniach w Nowosybirsku.
Po drugie, autor raportu Siergiej Zaika jest członkiem Komitetu Programowego naszej nowej konferencji na temat zarządzania wiedzą Konferencja Wiedzy. Konferencja jest jednodniowa, odbędzie się 26 kwietnia, ale jej program jest bardzo intensywny.
A stanie się to w maju PHP Rosja и RIT++ (z dołączonym DevOpsConf) - możesz tam również zasugerować swój temat, podzielić się swoimi doświadczeniami i ponarzekać na wypchane rożki.

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

Dodaj komentarz