Come smettere di preoccuparsi e iniziare a vivere senza monoliti

Come smettere di preoccuparsi e iniziare a vivere senza monoliti

Tutti noi amiamo le storie. Ci piace raccontare i nostri successi, battaglie o semplicemente esperienze passate attorno al fuoco.

Oggi è esattamente quel tipo di giorno. E anche se ora non sei attorno al fuoco, abbiamo una storia per te. Una storia su come abbiamo iniziato a lavorare con il database su Tarantool.

Tanto tempo fa, nella nostra azienda, c'erano un paio di "monoliti" e un "soffitto" condiviso, verso cui questi monoliti si avvicinavano lentamente ma inesorabilmente, limitando la crescita e lo sviluppo della nostra azienda. E c'era una chiara consapevolezza: un giorno saremmo stati costretti a batterci contro questo soffitto.

Ora abbiamo un'ideologia che promuove la separazione di tutto, dall'hardware alla logica aziendale. Di conseguenza, abbiamo, ad esempio, due datacenter praticamente indipendenti a livello di rete. Ma allora tutto era molto diverso.

Oggi abbiamo a disposizione una miriade di strumenti e risorse come CI/CD, K8S, e così via, per apportare modifiche. All'epoca dei "monoliti", non avevamo bisogno di tutti questi termini strani. Era sufficiente correggere semplicemente il "database".

Ma il tempo è andato avanti, e il numero delle richieste è aumentato con esso, arrivando a superare le nostre capacità. Entrando nei mercati dell'area CIS, il carico sulla CPU del primo monolito non scendeva mai sotto il 90%, mentre il RPS si mantenva attorno a 2400. E non si trattava solo di piccole selezioni, ma di grandi richieste con un sacco di controlli e JOIN che potevano coinvolgere quasi metà dei dati a causa di un grande IO.

Quando sono iniziate le vere vendite del "Black Friday" — e Wildberries è stata una delle prime in Russia a farlo — la situazione è diventata davvero critica. Infatti, il carico in quei giorni triplica.
Oh, i tempi dei "monoliti"! Sono certo che anche tu hai vissuto esperienze simili e non riesci ancora a capire come sia potuto succedere.

Che ci vuoi fare, anche la moda riguarda le tecnologie. Solo cinque anni fa, ci è stato necessario ripensare una di queste mode, il sito esistente su .NET e il server MS SQL, che custodiva gelosamente tutta la logica del sito stesso. Custodiva così bene che dividere un tale monolito è diventato un processo lungo e decisamente complicato.
Una breve digressione.

Durante vari eventi dico: "Se non hai diviso un monolito, significa che non sei cresciuto!" Mi piacerebbe conoscere la tua opinione in merito, scrivila nei commenti.

E il tuono si fece sentire

Tornando al nostro "fuoco". Per distribuire il carico della funzionalità "monolitica", abbiamo deciso di suddividere il sistema in microservizi basati su tecnologie open source. Perché, almeno, il loro scalabilità è più economica. E avevamo ben chiaro che sarebbe stato necessario scalare (e non poco) al 100%. Già a quel tempo avevamo iniziato a entrare nei mercati dei paesi vicini e il numero di registrazioni e ordini iniziava a crescere ancora di più.

Analizzando i primi candidati ad uscire dal monolito verso i microservizi, abbiamo compreso che nell'80% dei casi le scritture avvengono per il 99% tramite sistemi di back office, mentre le letture provengono dalla fascia anteriore. Questo riguardava principalmente un paio di sottosistemi importanti per noi: i dati degli utenti e il sistema di calcolo del costo finale dei prodotti in base alle informazioni su sconti e coupon.

A margine. Oggi è spaventoso da immaginare, ma oltre ai sottosistemi menzionati, dal nostro monolito sono stati estratti anche cataloghi di prodotti, carrelli, sistemi di ricerca, filtri per cataloghi e vari sistemi di raccomandazione. Ogni sistema ha le proprie classi specializzate, ma un tempo vivevano tutti insieme in un unico "spazio".

Abbiamo pianificato di estrarre subito i dati sui nostri clienti in un sistema shardato. L'estrazione della funzionalità di calcolo del costo finale dei prodotti richiedeva una buona scalabilità nella lettura, poiché comportava il maggior carico in RPS ed era molto complessa per il database (in quanto coinvolti molti dati nel processo di calcolo).

Di conseguenza, abbiamo sviluppato uno schema che si adatta bene a Tarantool.

A quel tempo, per i microservizi sono stati scelti schemi di lavoro con più datacenter su macchine virtuali e hardware. Come mostrato nelle immagini, sono state utilizzate configurazioni di replica di Tarantool sia in modalità master-master che master-slave.

Come smettere di preoccuparsi e iniziare a vivere senza monoliti
Architettura. Opzione 1. Servizio utenti

Attualmente ci sono 24 shard, ognuno con 2 istanze (una per ogni datacenter), tutte in modalità master-master.

Sopra il database ci sono applicazioni che accedono alle repliche del database. Le applicazioni comunicano con Tarantool tramite la nostra libreria personalizzata, che implementa l'interfaccia del driver Go per Tarantool. Essa vede tutte le repliche e può lavorare con il master sia in lettura che in scrittura. Fondamentalmente, implementa un modello di replica set, con logica per la selezione delle repliche, retry, circuit breaker e rate limiting.

Inoltre, è possibile configurare la politica di selezione della replica per ogni shard. Ad esempio, tramite round-robin.

Come smettere di preoccuparsi e iniziare a vivere senza monoliti
Architettura. Opzione 2. Servizio per il calcolo del costo finale del prodotto.

Alcuni mesi fa, la maggior parte delle richieste per il calcolo del costo finale dei prodotti sono state trasferite a un nuovo servizio che, in sostanza, funziona senza database, ma fino a quel momento il 100% era gestito da un servizio con Tarantool sotto il cofano.

Il database del servizio è composto da 4 master, che sincronizzano i dati, e ognuno di questi master distribuisce i dati alle repliche in sola lettura tramite replicazione. Ogni master ha circa 15 di queste repliche.

In entrambe le schemi, in caso di inaccessibilità di un data center, l'applicazione può ottenere dati dall'altro.

Va notato che in Tarantool la replicazione è piuttosto flessibile e configurabile in runtime. In altri sistemi ci sono stati problemi. Ad esempio, in PostgreSQL la modifica dei parametri max_wal_senders e max_replication_slots richiede il riavvio del master, il che in alcuni casi può causare interruzioni delle connessioni tra l'applicazione e il DBMS.

Cerca e troverai!

Perché non abbiamo fatto "come le persone normali" e abbiamo scelto un approccio atipico? Dipende da cosa si considera normale. Molti realizzano cluster con Mongo e li distribuiscono su tre data center geo-distribuiti.

All'epoca, avevamo già due progetti su Redis. Il primo era una cache, mentre il secondo rappresentava uno storage persistente per dati non troppo critici. Con quest'ultimo ci sono stati molti problemi, in parte a causa nostra. A volte grandi volumi di dati erano memorizzati in una chiave e di tanto in tanto il sito ne risentiva. Questa sistema veniva utilizzato in modalità master-slave. Ci sono stati molti casi in cui qualcosa accadeva al master e la replicazione si interrompeva.

Quindi Redis è buono per compiti stateless, non per stateful. In linea di massima, consentiva di risolvere la maggior parte dei problemi, ma solo se si trattava di soluzioni key-value con un paio di indici. Tuttavia, la persistenza e la replicazione di Redis a quel tempo erano piuttosto deludenti. Inoltre, ci sono stati reclami riguardo le prestazioni.

Abbiamo considerato MySQL e PostgreSQL. Ma il primo non ha trovato spazio da noi, mentre il secondo è di per sé un prodotto piuttosto complesso e costruire servizi semplici su di esso sarebbe stato poco pratico.
Abbiamo provato RIAK, Cassandra, persino un database grafico. Tutte queste sono soluzioni di nicchia che non si adattavano al ruolo di strumento universale per la creazione di servizi.

Alla fine ci siamo fermati su Tarantool.

Ci siamo rivolti a esso quando era alla versione 1.6. Siamo rimasti colpiti dalla sinergia tra key-value e funzionalità di un database relazionale. Ci sono indici secondari, transazioni e spazi, che sono come tabelle, ma non semplici, poiché possono contenere un numero variabile di colonne. Ma la killer feature di Tarantool erano gli indici secondari combinati con key-value e transazionalità.

Allo stesso modo, ha giocato un ruolo importante la comunità di lingua russa, pronta ad aiutare in chat. Ne abbiamo approfittato attivamente e vivevamo praticamente in chat. Inoltre, è fondamentale menzionare la buona persistenza senza evidenti problemi e bug. Se guardiamo alla nostra storia con Tarantool, ci sono stati molti dolori e fallimenti con la replicazione, ma non abbiamo mai perso dati a causa sua!

L'implementazione è iniziata in modo difficile.

A quel tempo, il nostro stack di sviluppo principale era .NET, per il quale non esisteva un connettore per Tarantool. Abbiamo subito iniziato a lavorare su Go. Anche con Lua andava piuttosto bene. Il problema principale era il debug: in .NET la situazione era ottimale, ma immergersi nel mondo di embedded Lua, con solo i log disponibili e senza alcun debug, era complicato. Inoltre, per qualche motivo la replicazione si rompeva periodicamente, e abbiamo dovuto approfondire il funzionamento del motore di Tarantool. In questo ci ha aiutato la chat, in misura minore la documentazione, a volte abbiamo guardato il codice. A quel tempo, la documentazione non era delle migliori.

Così, nel corso di alcuni mesi, siamo riusciti a fare esperienza e ottenere risultati soddisfacenti nel lavoro con Tarantool. Abbiamo formalizzato in git le soluzioni standard che aiutavano nella creazione di nuovi microservizi. Ad esempio, quando sorgeva la necessità di realizzare un nuovo microservizio, lo sviluppatore guardava il codice sorgente della soluzione di riferimento nel repository, e la creazione di un nuovo microservizio richiedeva non più di una settimana.

Erano tempi particolari. All'epoca potevi avvicinarti all'amministratore al tavolo accanto e chiedere: «Dammi una virtual machine». Dopo circa trenta minuti, la macchina era già a tua disposizione. Ti connettevi da solo, installavi tutto, e veniva instradato il traffico verso di essa.

Oggi non funziona più così: bisogna attivare il monitoraggio del servizio, registrare le attività, coprire la funzionalità con test, ordinare una virtual machine o installarla in Kubernetes, ecc. In generale, sarà meglio, anche se più lungo e complicato.

Dividi e conquista. Com'è la situazione con Lua?

C'era un serio dilemma: alcune squadre non riuscivano a gestire l'implementazione di modifiche nei servizi con una grande quantità di logica in Lua. Spesso questo comportava l'inefficienza del servizio.

In altre parole, gli sviluppatori preparano una modifica. Tarantool inizia a eseguire la migrazione, mentre la replica è ancora con il vecchio codice; viene ricevuto tramite replica qualche DDL o altro, e il codice si rompe semplicemente, perché non è stato previsto. Di conseguenza, la procedura di aggiornamento per gli amministratori era scritta su un foglio A4: fermare la replica, aggiornare questo, riattivare la replica, disattivare qui, aggiornare lì. Un vero incubo!

Di conseguenza, ora cerchiamo di non utilizzare Lua. Semplicemente tramite iproto (protocollo binario per l'interazione con il server), e basta. Forse è una mancanza di conoscenze da parte degli sviluppatori, ma da questa prospettiva il sistema è complesso.

Non seguiamo sempre ciecamente questo scenario. Oggi per noi non esiste più un bianco o nero: o tutto in Lua, o tutto in Go. Comprendiamo già come combinare le due cose per evitare problemi di migrazione in futuro.

Dove è attualmente presente Tarantool?
Tarantool è utilizzato nel servizio di calcolo del prezzo finale dei prodotti tenendo conto dei coupon sconto, noto come «Promotizer». Come detto in precedenza, adesso sta uscendo di scena: sarà sostituito da un nuovo servizio di catalogo con prezzi pre-calcolati, ma fino a sei mesi fa, tutti i calcoli venivano effettuati in «Promotizer». Prima, metà della sua logica era scritta in Lua. Due anni fa, il servizio è stato trasformato in un repository, e la logica è stata riscritta in Go, poiché la meccanica degli sconti è cambiata e il servizio necessitava di maggiore prestazioni.

Uno dei servizi più critici è il profilo utente. Cioè, tutti gli utenti di Wildberries vengono memorizzati in Tarantool, circa 50 milioni. Un sistema shardizzato per ID utente, distribuito su diversi data center con l'ausilio di servizi Go.
In passato, il leader per RPS era «Promotizer», raggiungeva fino a 6.000 richieste. A un certo punto avevamo 50-60 istanze. Ora, però, il leader per RPS è il profilo utente, che raggiunge circa 12.000. In questo servizio viene applicato uno sharding personalizzato suddiviso per range di ID utente. Il servizio gestisce più di 20 macchine, ma sono troppe; prevediamo di ridurre le risorse dedicate, poiché bastano le capacità di 4-5 macchine.

Il servizio delle sessioni è il nostro primo servizio su vshard e Cartridge. L'impostazione di vshard e l'aggiornamento di Cartridge hanno richiesto un certo impegno, ma alla fine tutto ha funzionato.

Il servizio per visualizzare diversi banner sul sito e nell'app mobile è stato uno dei primi a essere rilasciato direttamente su Tarantool. Questo servizio è notevole perché ha circa 6-7 anni, è ancora operativo e non è mai stato riavviato. Sono stati utilizzati la replica master-master. Non è mai successo nulla di rotto.

C'è un esempio di utilizzo di Tarantool per funzionalità di prontuari rapidi in un sistema di magazzino, per verificare rapidamente informazioni in alcuni casi. Abbiamo provato a usare Redis per questo, ma i dati in memoria occupavano più spazio rispetto a Tarantool.

I servizi della lista d'attesa, delle iscrizioni ai clienti, delle attuali storie di moda e dei prodotti in sospeso funzionano anche con Tarantool. L'ultimo servizio occupa in memoria circa 120 GB. È il servizio con il maggior volume tra quelli elencati.

Conclusione

Grazie agli indici secondari combinati con key-value e transazionalità, Tarantool è ottimo per architetture basate su microservizi. Tuttavia, abbiamo incontrato difficoltà quando implementavamo modifiche nei servizi con una grande quantità di logica in Lua: i servizi smettevano spesso di funzionare. Non siamo riusciti a superare questo problema, e col tempo siamo arrivati a varie combinazioni di Lua e Go: sappiamo dove utilizzare un linguaggio e dove l'altro.

Ulteriori letture sull'argomento

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster