Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Hej Habra!

Przypominamy, że podążając za książką o Kafka opublikowaliśmy równie interesującą pracę o bibliotece API strumieni Kafka.

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Na razie społeczność dopiero poznaje ograniczenia tego potężnego narzędzia. Tak więc niedawno ukazał się artykuł, którego tłumaczenie chcielibyśmy Państwu przedstawić. Z własnego doświadczenia autor opowiada, jak zamienić Kafka Streams w rozproszony magazyn danych. Miłego czytania!

Biblioteka Apache'a Strumienie Kafki używany na całym świecie w przedsiębiorstwach do rozproszonego przetwarzania strumieniowego na platformie Apache Kafka. Jednym z niedocenianych aspektów tego frameworka jest to, że pozwala on przechowywać stan lokalny generowany w oparciu o przetwarzanie wątków.

W tym artykule opowiem, jak naszej firmie udało się z zyskiem wykorzystać tę szansę, opracowując produkt zapewniający bezpieczeństwo aplikacji w chmurze. Wykorzystując Kafka Streams stworzyliśmy mikrousługi stanu współdzielonego, z których każdy stanowi odporne na awarie i wysoce dostępne źródło wiarygodnych informacji o stanie obiektów w systemie. Dla nas jest to krok naprzód zarówno pod względem niezawodności, jak i łatwości wsparcia.

Jeśli interesuje Cię alternatywne podejście, które pozwala na wykorzystanie jednej centralnej bazy danych do obsługi stanu formalnego obiektów, przeczytaj, będzie ciekawie...

Dlaczego uznaliśmy, że nadszedł czas, aby zmienić sposób pracy ze stanem współdzielonym

Musieliśmy utrzymywać stan różnych obiektów na podstawie raportów agentów (np. czy witryna była atakowana)? Przed migracją do Kafka Streams często polegaliśmy na jednej centralnej bazie danych (+ API usługi) do zarządzania stanem. To podejście ma swoje wady: sytuacje intensywne na randkach utrzymanie spójności i synchronizacji staje się prawdziwym wyzwaniem. Baza danych może stać się wąskim gardłem lub w nim skończyć warunki wyścigu i cierpieć na nieprzewidywalność.

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Rysunek 1: Typowy scenariusz stanu podzielonego widziany przed przejściem do
Kafka i Kafka Streams: agenci przekazują swoje poglądy za pośrednictwem interfejsu API, zaktualizowany stan jest obliczany za pośrednictwem centralnej bazy danych

Poznaj strumienie Kafka, ułatwiające tworzenie mikrousług stanu współdzielonego

Około rok temu postanowiliśmy dokładnie przyjrzeć się scenariuszom naszych wspólnych stanów, aby rozwiązać te problemy. Od razu postanowiliśmy wypróbować Kafka Streams – wiemy, jak bardzo jest skalowalna, wysoce dostępna i odporna na awarie, jaką bogatą funkcjonalność oferuje streaming (transformacje, także stanowe). Właśnie tego potrzebowaliśmy, nie wspominając o tym, jak dojrzały i niezawodny stał się system przesyłania wiadomości w Kafce.

Każda z utworzonych przez nas mikrousług stanowych została zbudowana na podstawie instancji Kafka Streams z dość prostą topologią. Składał się z 1) źródła 2) procesora ze stałą pamięcią klucz-wartość 3) ujścia:

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Rysunek 2: Domyślna topologia naszych instancji przesyłania strumieniowego dla mikrousług stanowych. Należy pamiętać, że znajduje się tu również repozytorium zawierające metadane dotyczące planowania.

W tym nowym podejściu agenci tworzą wiadomości, które są wprowadzane do tematu źródłowego, a konsumenci — powiedzmy usługa powiadamiania pocztą — otrzymują obliczony stan współdzielony przez ujście (temat wyjściowy).

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Rysunek 3: Nowy przykładowy przebieg zadań dla scenariusza z udostępnionymi mikrousługami: 1) agent generuje komunikat, który dociera do tematu źródłowego platformy Kafka; 2) mikrousługa ze stanem współdzielonym (przy użyciu Kafka Streams) przetwarza go i zapisuje obliczony stan do końcowego tematu Kafki; po czym 3) konsumenci akceptują nowy stan

Hej, ten wbudowany magazyn klucz-wartość jest naprawdę bardzo przydatny!

Jak wspomniano powyżej, nasza topologia stanu współdzielonego zawiera składnicę klucz-wartość. Znaleźliśmy kilka opcji jego wykorzystania, a dwie z nich opisano poniżej.

Opcja nr 1: Użyj do obliczeń magazynu klucz-wartość

Nasz pierwszy magazyn klucz-wartość zawierał dane pomocnicze potrzebne do obliczeń. Przykładowo w niektórych przypadkach o państwie wspólnym decydowała zasada „większości głosów”. W repozytorium mogą znajdować się wszystkie najnowsze raporty agentów o stanie jakiegoś obiektu. Następnie, gdy otrzymamy nowy raport od tego czy innego agenta, możemy go zapisać, pobrać raporty od wszystkich innych agentów o stanie tego samego obiektu z pamięci i powtórzyć obliczenia.
Rysunek 4 poniżej pokazuje, jak udostępniliśmy magazyn kluczy/wartości metodzie przetwarzania procesora, aby można było następnie przetworzyć nowy komunikat.

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Ilustracja 4: Otwieramy dostęp do magazynu klucz-wartość dla metody przetwarzania procesora (po tym każdy skrypt współpracujący ze stanem współdzielonym musi implementować metodę doProcess)

Opcja nr 2: Tworzenie interfejsu API CRUD na strumieniach Kafka

Po ustaleniu podstawowego przepływu zadań zaczęliśmy pisać RESTful CRUD API dla naszych mikrousług stanu współdzielonego. Chcieliśmy mieć możliwość pobrania stanu niektórych lub wszystkich obiektów, a także ustawienia lub usunięcia stanu obiektu (przydatne przy obsłudze backendu).

Aby obsługiwać wszystkie interfejsy API Get State, za każdym razem, gdy musieliśmy ponownie obliczyć stan podczas przetwarzania, przechowywaliśmy go przez długi czas we wbudowanym magazynie klucz-wartość. W tym przypadku zaimplementowanie takiego API przy użyciu pojedynczej instancji strumieni Kafka staje się dość proste, jak pokazano na poniższej liście:

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Rysunek 5: Korzystanie z wbudowanej składnicy klucz-wartość w celu uzyskania wstępnie obliczonego stanu obiektu

Aktualizacja stanu obiektu poprzez API jest również łatwa do wdrożenia. Zasadniczo wszystko, co musisz zrobić, to utworzyć producenta Kafki i użyć go do utworzenia rekordu zawierającego nowy stan. Dzięki temu wszystkie wiadomości generowane poprzez API będą przetwarzane w taki sam sposób, jak te otrzymane od innych producentów (np. agentów).

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Rysunek 6: Możesz ustawić stan obiektu za pomocą producenta Kafki

Mała komplikacja: Kafka ma wiele partycji

Następnie chcieliśmy rozłożyć obciążenie przetwarzaniem i poprawić dostępność, udostępniając klaster mikrousług o stanie współdzielonym według scenariusza. Konfiguracja była prosta: kiedy już skonfigurowaliśmy wszystkie instancje do działania z tym samym identyfikatorem aplikacji (i tymi samymi serwerami startowymi), prawie wszystko inne zostało wykonane automatycznie. Określiliśmy również, że każdy temat źródłowy będzie składał się z kilku partycji, tak aby do każdej instancji można było przypisać podzbiór takich partycji.

Wspomnę też, że powszechną praktyką jest wykonywanie kopii zapasowej magazynu stanu, aby np. w przypadku odzyskiwania po awarii przenieść tę kopię do innej instancji. Dla każdego magazynu stanu w strumieniach Kafka tworzony jest zreplikowany temat z dziennikiem zmian (który śledzi lokalne aktualizacje). W ten sposób Kafka stale tworzy kopię zapasową magazynu stanowego. Dlatego w przypadku awarii tej czy innej instancji Kafka Streams magazyn stanu można szybko przywrócić w innej instancji, gdzie trafią odpowiednie partycje. Nasze testy wykazały, że odbywa się to w ciągu kilku sekund, nawet jeśli w sklepie znajdują się miliony rekordów.

Przechodząc od pojedynczej mikrousługi ze stanem udostępnionym do klastra mikrousług, wdrożenie interfejsu API Get State staje się mniej proste. W nowej sytuacji magazyn stanu każdej mikrousługi zawiera tylko część ogólnego obrazu (te obiekty, których klucze zostały zmapowane na konkretną partycję). Musieliśmy określić, która instancja zawiera stan potrzebnego obiektu, i zrobiliśmy to w oparciu o metadane wątku, jak pokazano poniżej:

Nie tylko przetwarzanie: jak stworzyliśmy rozproszoną bazę danych ze strumieni Kafka i co z tego wynikło

Rysunek 7: Korzystając z metadanych strumienia, określamy, z której instancji należy zapytać o stan żądanego obiektu; podobne podejście zastosowano w przypadku interfejsu API GET ALL

Kluczowe ustalenia

Magazyny stanowe w Kafka Streams mogą służyć de facto jako rozproszona baza danych,

  • stale powielane w Kafce
  • Na takim systemie można łatwo zbudować interfejs API CRUD
  • Obsługa wielu partycji jest nieco bardziej skomplikowana
  • Możliwe jest również dodanie jednego lub większej liczby magazynów stanów do topologii przesyłania strumieniowego w celu przechowywania danych pomocniczych. Opcji tej można użyć do:
  • Długotrwałe przechowywanie danych potrzebnych do obliczeń podczas przetwarzania strumienia
  • Długoterminowe przechowywanie danych, które mogą być przydatne przy następnym udostępnieniu instancji przesyłania strumieniowego
  • wiele więcej...

Te i inne zalety sprawiają, że strumienie Kafka doskonale nadają się do utrzymywania stanu globalnego w systemie rozproszonym, takim jak nasz. Usługa Kafka Streams okazała się bardzo niezawodna w środowisku produkcyjnym (od czasu jej wdrożenia praktycznie nie zaobserwowaliśmy utraty wiadomości) i jesteśmy pewni, że na tym jej możliwości się nie skończą!

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

Dodaj komentarz