Jak przestać się martwić i zacząć żyć bez monolitu

Jak przestać się martwić i zacząć żyć bez monolitu

Wszyscy lubimy historie. Lubimy, siedząc przy ognisku, opowiadać o swoich dawnych zwycięstwach, bitwach lub po prostu o swoim doświadczeniu.

Dziś jest właśnie taki dzień. I choć nie siedzisz przy ognisku, mamy dla ciebie historię. Opowieść o tym, jak zaczęliśmy pracować z magazynem na Tarantool.

Dawno temu w naszej firmie były dwa «monolity» i jeden wspólny «sufit», do którego te monolity powoli, ale pewnie się zbliżały, ograniczając rozwój naszej firmy. I było jedno zrozumienie: pewnego dnia mocno uderzymy w ten sufit.

Teraz panuje u nas ideologia dzielenia wszystkiego, od sprzętu po logikę biznesową. W wyniku tego mamy na przykład dwa centra danych, praktycznie niezależne na poziomie sieci. A wtedy wszystko wyglądało zupełnie inaczej.

Obecnie istnieje mnóstwo narzędzi i środków do wprowadzania zmian, takich jak CI/CD, K8S itd. W czasach «monolitu» nie potrzebowaliśmy tak wielu obcych słów. Wystarczyło po prostu poprawić «magazyn» w bazie.

Jednak czas mijał, a liczba zapytań rosła razem z nim, często przekraczając nasze możliwości. Przy wejściu na rynek krajów WNP obciążenie procesora bazy danych pierwszego monolitu nigdy nie spadało poniżej 90%, a RPS utrzymywały się na poziomie 2400. I nie były to tylko małe selekcje, ale ogromne zapytania z mnóstwem sprawdzeń i JOIN'ów, które mogły przeszukać niemal połowę danych w kontekście dużego IO.

Kiedy na scenie zaczęły się pojawiać prawdziwe wyprzedaże na «Czarny piątek» — a Wildberries jednymi z pierwszych w Rosji je organizował — sytuacja stała się bardzo smutna. W takie dni obciążenie wzrasta trzy razy.
Ojej, te «monolitowe czasy»! Jestem pewien, że i ty się z tym spotkałeś i wciąż nie możesz zrozumieć, jak to mogło się zdarzyć.

Co tu zrobić — moda dotyczy też technologii. Jeszcze pięć lat temu musieliśmy przewartościować jedną z takich mod w postaci istniejącej strony na .NET i MS SQL serwerze, który starannie przechowywał całą logikę działania samej strony. Przechowywał tak starannie, że rozdzielanie tego monolitu okazało się długim i bardzo trudnym przyjemnością.
Małe przerywnik.

Na różnego rodzaju wydarzeniach mówię: „jeśli nie odcięliście monolitu, to znaczy, że nie rozwijaliście się!” Ciekawe jest wasze zdanie na ten temat, proszę napiszcie je w komentarzach.

I grzmoty zadudniły

Wróćmy do naszego „ogniska”. Aby rozłożyć obciążenie „monolitycznej” funkcjonalności, postanowiliśmy podzielić system na mikrousługi, oparte na technologiach opensource. Ponieważ ich skalowanie jest przynajmniej tańsze. A przekonanie, że będziemy musieli skalować (i to znacząco) mieliśmy na 100 %. Już wówczas udało się wejść na rynki sąsiednich krajów, a liczba rejestracji, jak i liczba zamówień, zaczęła rosnąć jeszcze bardziej.

Analizując pierwszych kandydatów do wyłączenia z monolitu w mikrousługi, zrozumieliśmy, że w 80% zapis w nich pochodzi w 99% z systemów back office, a odczyt — z frontu. W pierwszej kolejności dotyczyło to kilku ważnych dla nas podsystemów — danych użytkowników oraz systemu obliczania ostatecznych kosztów towarów na podstawie informacji o dodatkowych zniżkach i kuponach dla klientów.

Na prawach dygresji. Teraz strach pomyśleć, ale oprócz wspomnianych podsystemów, z naszego monolitu zostały również wydzielone katalogi produktów, koszyk użytkownika, system wyszukiwania produktów, system filtrowania katalogów produktów oraz różne systemy rekomendacji. Dla każdego z nich istnieją osobne klasy ściśle dostosowanych systemów, ale kiedyś wszystkie żyły w jednym „domku”.

Planowaliśmy wydzielić dane o naszych klientach od razu do systemu shardowego. Natomiast wydzielenie funkcjonalności obliczania ostatecznych kosztów towarów wymagało dobrej skalowalności w odczycie, ponieważ generowało największe obciążenie RPS i było najtrudniejsze w realizacji dla bazy (bardzo dużo danych jest zaangażowanych w proces obliczeń).

W wyniku tego powstał schemat, który dobrze współgrał z Tarantool.

W tym czasie do pracy mikrousług wybrano schematy działania z wieloma centrami danych na maszynach wirtualnych i fizycznych. Jak pokazano na rysunkach, zastosowano różne warianty replikacji Tarantool zarówno w trybie master-master, jak i master-slave.

Jak przestać się martwić i zacząć żyć bez monolitu
Architektura. Wariant 1. Usługa użytkowników.

Na obecny moment to 24 shardy, w każdym z nich po 2 instancje (po jednej na każdy DC), wszystkie w trybie master-master.

Nad bazą danych znajdują się aplikacje, które komunikują się z replikami bazy danych. Aplikacje współpracują z Tarantool poprzez naszą własną bibliotekę, która реализuje interfejs Go-drivera Tarantool. Widzi wszystkie repliki i może pracować z masterem w trybie odczytu i zapisu. W zasadzie wdraża model zestawu replik, w który dodano logikę wyboru replik, wykonywania ponownych prób, circuit breaker i limitu prędkości.

Istnieje możliwość konfigurowania polityki wyboru repliki w obrębie shardu. Na przykład, round-robin.

Jak przestać się martwić i zacząć żyć bez monolitu
Architektura. Opcja 2. Usługa obliczania końcowej wartości produktu

Kilka miesięcy temu znaczna część zapytań dotyczących obliczania końcowej wartości produktów przeszła do nowej usługi, która zasadniczo działa bez baz danych, ale jakiś czas temu 100% obliczeń przeprowadzała usługa z Tarantool pod maską.

Baza danych usługi to 4 mastery, do których synchronizator zbiera dane, a każdy z tych masterów przez replikację przekazuje dane do replik tylko do odczytu. Każdy master ma około 15 takich replik.

W obu schematach, w przypadku niedostępności jednego centrum danych, aplikacja może nadal otrzymywać dane z drugiego.

Warto zauważyć, że w Tarantool replikacja jest dość elastyczna i konfigurowana w czasie rzeczywistym. W innych systemach napotykało się trudności. Na przykład, zmieniając parametry max_wal_senders i max_replication_slots w PostgreSQL, wymaga to ponownego uruchomienia mastera, co w niektórych przypadkach może spowodować zerwanie połączeń między aplikacją a bazą danych.

Szukajcie, a znajdziecie!

Dlaczego nie zrobiliśmy „jak normalni ludzie” i nie wybraliśmy nietypowego sposobu? Cóż, co uważać za normalne. Wiele osób tworzy klaster z MongoDB i rozkłada go na trzy geograficznie rozproszone centra danych.

W tamtym czasie mieliśmy już dwa projekty na Redis. Pierwszy to pamięć podręczna, a drugi był persistentnym magazynem dla mniej krytycznych danych. Z tym ostatnim mieliśmy do czynienia ze znacznymi trudnościami, częściowo z naszej winy. Czasami dość duże ilości danych były składowane w jednym kluczu, a czasami strona miała problemy. Ten system wykorzystywaliśmy w wariancie master-slave. I było wiele sytuacji, gdy coś działo się z masterem i replikacja ulegała awarii.

Redis jest więc odpowiedni do zadań bezstanowych, a nie do zadań stanu. W zasadzie pozwalał rozwiązać większość problemów, ale tylko wtedy, gdy chodziło o rozwiązania typu key-value z kilkoma indeksami. Jednak w tamtym czasie Redis miał dość słabą persystencję i replikację. Poza tym były zastrzeżenia co do wydajności.

Myśleliśmy o MySQL i PostgreSQL. Jednak MySQL jakoś się u nas nie przyjął, a PostgreSQL to dość zaawansowany produkt, na którym budowanie prostych usług byłoby nieefektywne.
Próbowaliśmy RIAK, Cassandrę, nawet bazę danych grafową. Wszystkie te rozwiązania są dość niszowe i nie nadawały się na uniwersalne narzędzie do tworzenia usług.

Ostatecznie zdecydowaliśmy się na Tarantool.

Skontaktowaliśmy się z nim, gdy był w wersji 1.6. Zainteresował nas symbioza key-value i funkcjonalności relacyjnej bazy danych. Istnieją indeksy drugorzędne, transakcje i spacje, które są jak tabele, ale nieco bardziej skomplikowane, można w nich przechowywać różną liczbę kolumn. Jednak kluczową cechą Tarantool były indeksy drugorzędne w połączeniu z key-value i transakcyjnością.

Również ważną rolę odegrała pomocna, rosyjskojęzyczna społeczność, gotowa do wsparcia na czacie. Aktywnie z tego korzystaliśmy i praktycznie żyliśmy na czacie. Nie można też zapominać o przyzwoitym działaniu bez rażących błędów czy usterek. Jeśli spojrzeć na naszą historię z Tarantool, mieliśmy wiele problemów i wpadek z replikacją, ale nigdy nie straciliśmy danych z jego powodu!

Wdrożenie zaczęło się trudno.

W tym czasie naszym głównym stosem do tworzenia aplikacji było .NET, do którego nie było konektora dla Tarantool. Od razu zaczęliśmy pisać coś na Go. Z Lua też szło całkiem nieźle. Głównym problemem w tamtym czasie była debugowanie: w .NET to działa świetnie, a później zanurzenie się w świat embedded Lua, gdy jedynym debuggowalnym narzędziem są logi, było trudne. Ponadto replikacja z jakiegoś powodu czasami się psuła, musieliśmy zagłębić się w działanie silnika Tarantool. W tym pomógł czat, w mniejszym stopniu — dokumentacja, czasami przeglądaliśmy kod. W tamtym czasie dokumentacja była dość słaba.

Tak w ciągu kilku miesięcy udało się zdobyć doświadczenie i osiągnąć zadowalające wyniki w pracy z Tarantool. Spisaliśmy w gicie wzorcowe realizacje, które pomagały w tworzeniu nowych mikroserwisów. Na przykład, gdy pojawiał się zadanie: stworzyć kolejny mikroserwis, programista zerkał na źródła wzorcowego rozwiązania w repozytorium, a stworzenie nowego zajmowało nie więcej niż tydzień.

To były szczególne czasy. Można było podejść do administratora przy sąsiednim stole i poprosić: „Daj mi wirtualkę”. Po około trzydziestu minutach maszyna była już u ciebie. Sam się łączyłeś, wszystko instalowałeś, a on kierował na nią ruch.

Dziś już tak się nie da: trzeba wdrożyć monitoring serwisu, logowanie, pokryć funkcjonalność testami, zamówić wirtualkę lub dostawę do Kubernetesa itd. Ogólnie, tak będzie lepiej, choć dłużej i bardziej kłopotliwe.

Podziel i rządź. Jak wygląda sytuacja z Lua?

Był poważny dylemat: niektórym zespołom nie udawało się niezawodnie wprowadzać zmian w serwisie z dużą ilością logiki na Lua. Często towarzyszyło temu niesprawność serwisu.

To znaczy, że programiści przygotowują jakieś zmiany. Tarantool zaczyna robić migrację, a replika ma jeszcze stary kod; przychodzi tam przez replikację jakiś DDL, coś jeszcze, a kod po prostu się psuje, bo nie zostało to uwzględnione. W rezultacie procedura aktualizacji u administratorów była zapisana na kartce A4: zatrzymać replikację, zaktualizować to, włączyć replikację, wyłączyć tu, zaktualizować tam. Koszmar!

Ostatecznie, teraz najczęściej staramy się nic nie robić na Lua. Po prostu za pomocą iproto (protokołu binarnego do interakcji z serwerem) i tyle. Może to brak wiedzy u programistów, ale z tej perspektywy system jest skomplikowany.

Nie zawsze ślepo podążamy za tym schematem. Dziś nie mamy czerni i bieli: albo wszystko na Lua, albo wszystko na Go. Już rozumiemy, jak można to połączyć, aby później nie mieć problemów z migracją.

Gdzie teraz jest Tarantool?
Tarantool jest używany w serwisie obliczania końcowej ceny towarów z uwzględnieniem kuponów rabatowych, zwanym „Promotajzer”. Jak już wcześniej wspominałem, teraz wychodzi z użycia: zastępuje go nowy serwis katalogowy z wstępnie obliczonymi cenami, ale jeszcze pół roku temu wszystkie obliczenia były realizowane w „Promotajzerze”. Kiedyś połowa jego logiki była napisana w Lua. Dwa lata temu z serwisu zrobiono magazyn, a logikę przepisano na Go, ponieważ mechanika działania rabatów nieco się zmieniła, a serwisowi brakowało wydajności.

Jednym z najważniejszych serwisów jest profil użytkownika. To znaczy, że wszyscy użytkownicy Wildberries są przechowywani w Tarantool, a jest ich około 50 milionów. System shardingowy według ID użytkownika, rozproszony po kilku centrach danych z obwódka w Go-serwisach.
Kiedyś liderem w RPS był „Promotajzer”, działając do 6 tysięcy zapytań. W pewnym momencie mieliśmy 50-60 instancji. Teraz jednak liderem w RPS są profile użytkowników, zbliżające się do 12 tysięcy. W tym serwisie stosowane jest niestandardowe shardingowanie z podziałem na zakresy ID użytkowników. Serwis obsługuje ponad 20 maszyn, ale to zbyt wiele, planujemy zmniejszyć przydzielone zasoby, ponieważ wystarczą mu moce 4-5 maszyn.

Serwis sesji to nasz pierwszy serwis na vshard i Cartridge. Konfiguracja vshard i aktualizacja Cartridge wymagały od nas określonych nakładów pracy, ale ostatecznie wszystko się udało.

Serwis do wyświetlania różnych banerów na stronie i w aplikacji mobilnej był jednym z pierwszych wydanych od razu na Tarantool. Ten serwis jest znany z tego, że ma około 6-7 lat, nadal działa i nigdy nie był restartowany. Stosowano replikację master-master. Nigdy nic się nie zepsuło.

Jest przykład użycia Tarantool do funkcjonalności szybkich spisów w systemie magazynowym, aby szybko weryfikować informacje w niektórych przypadkach. Próbowaliśmy używać do tego celu Redis, ale dane w pamięci zajmowały więcej miejsca niż w Tarantool.

Serwisy listy oczekiwania, subskrypcji klientów, modnych ostatnio relacji i odłożonych towarów również działają z Tarantool. Ostatni serwis zajmuje w pamięci około 120 GB. To najbardziej obszerny serwis spośród wymienionych.

Podsumowanie

Dzięki wtórnym indeksom w połączeniu z klucz-wartość oraz transakcyjnością, Tarantool doskonale nadaje się do architektur opartych na mikroserwisach. Napotkaliśmy jednak trudności, gdy wprowadzaliśmy zmiany w usługach z dużą ilością logiki w Lua — usługi często przestawały działać. Nie udało nam się tego pokonać, więc z czasem doszliśmy do różnych kombinacji Lua i Go: wiemy, gdzie warto użyć jednego języka, a gdzie innego.

Co jeszcze przeczytać na ten temat

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster