Kafka și microservicii: o prezentare generală

Kafka și microservicii: o prezentare generală

Salutare tuturor. În acest articol vă voi spune de ce noi cei de la Avito am ales-o pe Kafka în urmă cu nouă luni și ce este. Voi împărtăși unul dintre cazurile de utilizare - un broker de mesaje. Și, în sfârșit, să vorbim despre avantajele pe care le-am obținut în urma utilizării abordării Kafka ca serviciu.

problemă

Kafka și microservicii: o prezentare generală

În primul rând, puțin context. Cu ceva timp în urmă am început să ne îndepărtăm de arhitectura monolitică, iar acum Avito are deja câteva sute de servicii diferite. Ei au propriile lor depozite, propria lor stivă de tehnologie și sunt responsabili pentru partea lor din logica afacerii.

Una dintre problemele unui număr mare de servicii este comunicarea. Serviciul A dorește adesea să cunoască informațiile pe care le are Serviciul B. În acest caz, Serviciul A accesează Serviciul B printr-un API sincron. Serviciul B vrea să știe ce se întâmplă cu serviciile D și D, iar ei, la rândul lor, sunt interesați de serviciile A și B. Când există multe astfel de servicii „curioase”, conexiunile dintre ele se transformă într-o încurcătură.

În același timp, serviciul A poate deveni indisponibil în orice moment. Și ce ar trebui să facă serviciul B și toate celelalte servicii conectate la acesta în acest caz? Și dacă este necesar un lanț de apeluri sincrone secvențiale pentru a finaliza o operațiune de afaceri, probabilitatea de eșec a întregii operațiuni devine și mai mare (și cu cât lanțul este mai lung, cu atât este mai mare).

Alegerea tehnologiei

Kafka și microservicii: o prezentare generală

Bine, problemele sunt clare. Acestea pot fi eliminate prin crearea unui sistem de mesagerie centralizat între servicii. Acum fiecare dintre servicii trebuie să știe doar despre acest sistem de mesagerie. În plus, sistemul în sine trebuie să fie tolerant la erori și scalabil orizontal și, de asemenea, în caz de accidente, să acumuleze un tampon de acces pentru procesarea ulterioară.

Să selectăm acum tehnologia pe care va fi implementată livrarea mesajelor. Pentru a face acest lucru, să înțelegem mai întâi ce așteptăm de la el:

  • mesajele dintre servicii nu trebuie pierdute;
  • mesajele pot fi duplicate;
  • mesajele pot fi stocate și citite la o adâncime de câteva zile (buffer persistent);
  • serviciile se pot abona la datele de care sunt interesate;
  • mai multe servicii pot citi aceleași date;
  • mesajele pot conține încărcătură utilă detaliată și voluminoasă (transfer de stat efectuat de evenimente);
  • Uneori trebuie să garantați ordinea mesajelor.

A fost, de asemenea, extrem de important pentru noi să alegem cel mai scalabil și mai fiabil sistem cu un randament ridicat (cel puțin 100 de mesaje de câțiva kilobytes pe secundă).

În acest moment, ne-am luat rămas bun de la RabbitMQ (dificil de menținut stabil la rps mari), PGQ de la SkyTools (nu suficient de rapid și nu se scalează bine) și NSQ (nu persistent). Utilizăm toate aceste tehnologii în compania noastră, dar nu erau potrivite pentru problema rezolvată.

Apoi, am început să ne uităm la tehnologii care erau noi pentru noi - Apache Kafka, Apache Pulsar și NATS Streaming.

Pulsar a fost primul care a fost aruncat. Am decis că Kafka și Pulsar sunt soluții destul de asemănătoare. Și în ciuda faptului că Pulsar a fost testat de companii mari, este mai nou și oferă o latență mai mică (teoretic), am decis să lăsăm Kafka dintre acestea două ca standard de facto pentru astfel de sarcini. Probabil ne vom întoarce la Apache Pulsar în viitor.

Și acum au mai rămas doi candidați: NATS Streaming și Apache Kafka. Am studiat ambele soluții în detaliu și ambele erau potrivite pentru sarcină. Dar, până la urmă, ne-a fost teamă de relativa tinerețe a NATS Streaming (și de faptul că unul dintre principalii dezvoltatori, Tyler Treat, a decis să părăsească proiectul și să-și înceapă propriul - Liftbridge). În același timp, modul Clustering al NATS Streaming nu a oferit posibilitatea unei scalari orizontale puternice (probabil aceasta nu mai este o problemă după adăugarea modului de partiționare în 2017).

Cu toate acestea, NATS Streaming este o tehnologie cool scrisă în Go și susținută de Cloud Native Computing Foundation. Spre deosebire de Apache Kafka, nu are nevoie de Zookeeper pentru a funcționa (poate în curând acelaşi lucru se poate spune despre Kafka), deoarece implementează RAFT intern. În același timp, NATS Streaming este mai ușor de administrat. Nu excludem că vom reveni la această tehnologie în viitor.

Și totuși, astăzi câștigătorul nostru este Apache Kafka. În testele noastre, s-a dovedit a fi destul de rapid (mai mult de un milion de mesaje pe secundă pentru citire și scriere cu un volum de mesaje de 1 kilobyte), destul de fiabil, foarte scalabil și dovedit de experiența în producția de companii mari. În plus, Kafka susține cel puțin mai multe companii comerciale mari (noi, de exemplu, folosim versiunea Confluent), iar Kafka are și un ecosistem dezvoltat.

Privire de ansamblu Kafka

Înainte de a începe, aș dori să recomand imediat o carte excelentă - „Kafka: Ghidul definitiv” (există și o traducere în limba rusă, dar termenii sunt puțin năucitori). Conține informațiile de care aveți nevoie pentru a obține o înțelegere de bază a lui Kafka și chiar puțin mai mult. Documentația Apache și blogul Confluent sunt, de asemenea, bine scrise și ușor de citit.

Așa că haideți să vedem cum funcționează Kafka. Topologia de bază a lui Kafka constă din producător, consumator, broker și zookeeper.

Broker

Kafka și microservicii: o prezentare generală

Brokerul este responsabil pentru stocarea datelor dvs. Toate datele sunt stocate în formă binară, iar brokerul știe puțin despre ce sunt și care este structura lor.

Fiecare tip de eveniment logic este de obicei localizat într-un subiect separat. De exemplu, evenimentul de creare a unui anunț poate intra în subiectul item.created, iar evenimentul modificării acestuia poate intra în item.changed. Subiectele pot fi considerate ca clasificatori de evenimente. La nivel de subiect, puteți seta parametri de configurare precum:

  • cantitatea de date stocate și/sau vechimea acestora (retention.bytes, retention.ms);
  • factor de redundanță a datelor (factor de replicare);
  • dimensiunea maximă a unui mesaj (max.message.bytes);
  • numărul minim de replici consistente la care datele pot fi scrise într-un subiect (min.insync.replicas);
  • capacitatea de a efectua un failover pe o replică întârziată nesincronă cu potențială pierdere de date (unclean.leader.election.enable);
  • si multe altele (https://kafka.apache.org/documentation/#topicconfigs).

La rândul său, fiecare subiect este împărțit în una sau mai multe partiții. Evenimentele cad în cele din urmă în partide. Dacă există mai mult de un broker în cluster, atunci partițiile vor fi distribuite uniform între toți brokerii (pe cât posibil), ceea ce va permite ca sarcina de scriere și citire într-un singur subiect să fie scalată pe mai mulți brokeri simultan.

Pe disc, datele pentru fiecare partiție sunt stocate sub formă de fișiere segment, implicit egale cu un gigabyte (controlat prin log.segment.bytes). O caracteristică importantă este că datele sunt șterse din partiții (când se declanșează reținerea) în segmente (nu puteți șterge un eveniment dintr-o partiție, puteți șterge doar un întreg segment și doar pe cel inactiv).

Ingrijitor zoo

Zookeeper acționează ca un depozit de metadate și coordonator. El este cel care este capabil să spună dacă brokerii sunt în viață (puteți privi acest lucru prin ochii paznicului zoologic folosind zookeeper-shell cu comanda ls /brokers/ids), care broker este controlorul (get /controller), dacă partițiile sunt sincronizate cu replicile lor (get /brokers/topics/topic_name/partitions/partition_number/state). De asemenea, producătorul și consumatorul vor merge mai întâi la zookeeper pentru a afla pe ce broker sunt stocate subiectele și partițiile. În cazurile în care este specificat un factor de replicare mai mare de 1 pentru un subiect, zookeeper va indica care partiții sunt lideri (ele vor fi scrise și din care vor fi citite). În cazul unei eșecuri a brokerului, informațiile despre noile partiții lider vor fi înregistrate în zookeeper (din versiunea 1.1.0 asincron, si asta e important).

În versiunile mai vechi de Kafka, zookeeperul era, de asemenea, responsabil pentru stocarea compensațiilor, dar acum acestea sunt stocate într-un subiect special __consumer_offsets pe broker (deși puteți utiliza în continuare Zookeeper în aceste scopuri).

Cel mai simplu mod de a vă transforma datele într-un dovleac este să pierdeți informații de la zookeeper. Într-un astfel de scenariu, va fi foarte greu de înțeles ce să citești și de unde.

Producător

Producer este cel mai adesea un serviciu care scrie direct date în Apache Kafka. Producătorul selectează un subiect în care să-și stocheze mesajele subiectului și începe să-i scrie informații. De exemplu, producătorul ar putea fi un serviciu publicitar. În acest caz, va trimite evenimente precum „anunț creat”, „anunț actualizat”, „anunț șters”, etc. la subiecte tematice. Fiecare eveniment este o pereche cheie-valoare.

În mod implicit, toate evenimentele sunt distribuite între partițiile de subiecte folosind round-robin dacă cheia nu este specificată (pierderea ordinii) și prin MurmurHash (cheia) dacă cheia este prezentă (ordonarea într-o partiție).

Merită remarcat imediat că Kafka garantează ordinea evenimentelor doar într-un singur lot. Dar, în realitate, aceasta nu este adesea o problemă. De exemplu, puteți fi sigur că adăugați toate modificările la aceeași declarație într-o singură partiție (păstrând astfel ordinea acestor modificări în cadrul declarației). De asemenea, puteți trimite un număr de secvență într-unul dintre câmpurile evenimentului.

Consumator

Kafka și microservicii: o prezentare generală

Consumatorul este responsabil pentru preluarea datelor de la Apache Kafka. Dacă revenim la exemplul de mai sus, consumatorul ar putea fi un serviciu de moderare. Acest serviciu va fi abonat la subiectul serviciului publicitar, iar atunci când apare un nou anunț, îl va primi și îl va analiza pentru respectarea unor politici specificate.

Apache Kafka își amintește ce evenimente recente a primit consumatorul (un subiect de serviciu este folosit pentru aceasta __consumer__offsets), asigurându-se astfel că, dacă citirea are succes, consumatorul nu primește același mesaj de două ori. Cu toate acestea, dacă utilizați opțiunea enable.auto.commit = true și delegeți complet munca de urmărire a poziției consumatorului în subiect către Kafka, puteți pierde date. În codul de producție, cel mai adesea poziția consumatorului este controlată manual (dezvoltatorul controlează momentul în care trebuie să aibă loc comiterea evenimentului citit).

În cazurile în care un singur consumator nu este suficient (de exemplu, fluxul de evenimente noi este foarte mare), puteți adăuga mai mulți consumatori legând-i împreună într-un grup de consumatori. Un grup de consumatori este logic exact la fel ca un consumator, dar cu date distribuite între membrii grupului. Acest lucru permite fiecărui participant să-și ia partea de mesaje, reducând astfel viteza de citire.

Rezultatele testelor

Kafka și microservicii: o prezentare generală

Nu voi scrie prea mult text explicativ aici, voi împărtăși doar rezultatele obținute. Testarea a fost efectuată pe 3 mașini fizice (12 CPU, 384 GB RAM, 15k SAS DISK, 10 GBit/s Net), brokeri și zookeeper au fost dislocați în lxc.

Test de performanta

În timpul testării, s-au obținut următoarele rezultate.

  • Viteza de înregistrare a mesajelor de 1 KB simultan de către 9 producători este de 1300000 de evenimente pe secundă.
  • Viteza de citire simultană a mesajelor de 1 KB de către 9 consumatori este de 1500000 de evenimente pe secundă.

Testarea toleranței la erori

În timpul testării s-au obținut următoarele rezultate (3 brokeri, 3 gardieni).

  • Încetarea anormală a unuia dintre brokeri nu face ca clusterul să se oprească sau să devină indisponibil. Lucrările continuă ca de obicei, dar brokerii rămași au un volum mare de muncă.
  • Terminarea anormală a doi brokeri în cazul unui cluster de trei brokeri și min.isr = 2 duce la indisponibilitatea clusterului pentru scriere, dar accesibilă pentru citire. Dacă min.isr = 1, clusterul continuă să fie disponibil atât pentru citire, cât și pentru scriere. Cu toate acestea, acest mod contrazice cerința de securitate ridicată a datelor.
  • O oprire anormală a unuia dintre serverele Zookeeper nu face ca clusterul să se oprească sau să devină indisponibil. Lucrările continuă ca de obicei.
  • O oprire anormală a două servere Zookeeper face ca clusterul să fie indisponibil până când cel puțin unul dintre serverele Zookeeper este restaurat. Această afirmație este valabilă pentru un cluster Zookeeper de 3 servere. Drept urmare, după cercetări, s-a decis creșterea clusterului Zookeeper la 5 servere pentru a crește toleranța la erori.

Kafka ca serviciu

Kafka și microservicii: o prezentare generală

Suntem convinși că Kafka este o tehnologie excelentă care ne permite să rezolvăm sarcina care ne este atribuită (implementarea unui broker de mesaje). Cu toate acestea, am decis să interzicem accesul direct serviciilor la Kafka și l-am închis pe deasupra cu un serviciu de magistrală de date. De ce am făcut asta? De fapt, există destul de multe motive.

  • Data-bus a preluat toate sarcinile legate de integrarea cu Kafka (implementarea și configurarea consumatorilor și producătorilor, monitorizare, alertă, logare, scalare etc.). Astfel, integrarea cu brokerul de mesaje este cât se poate de simplă.

  • Data-bus ne-a permis să facem abstracție de la un anumit limbaj sau bibliotecă pentru a lucra cu Kafka.

  • Data-bus a permis altor servicii să abstragă stratul de stocare. Poate că la un moment dat îl vom schimba pe Kafka în Pulsar și nimeni nu va observa nimic (toate serviciile știu doar despre API-ul magistralei de date).

  • Data-bus a preluat validarea schemelor de evenimente.

  • Autentificarea este implementată folosind magistrala de date.

  • Sub acoperirea magistralei de date, putem actualiza în liniște versiunile Kafka fără timpi de nefuncționare, putem gestiona centralizat configurațiile producătorilor, consumatorilor, brokerilor etc.

  • Data-bus ne-a permis să adăugăm caracteristicile de care aveam nevoie și care nu sunt în Kafka (cum ar fi auditarea subiectelor, monitorizarea anomaliilor în cluster, crearea DLQ etc.).

  • Data-bus vă permite să implementați failover-ul central pentru toate serviciile.

În acest moment, pentru a începe să trimiteți evenimente către brokerul de mesaje, trebuie doar să conectați o mică bibliotecă la codul dvs. de serviciu. Asta este tot. Aveți capacitatea de a scrie, citi și scala cu o singură linie de cod. Întreaga implementare vă este ascunsă, cu doar câteva mânere de dimensiunea lotului ieșind în afară. Sub capotă, serviciul de magistrală de date ridică numărul necesar de instanțe de producător și consumatori în Kubernetes și le oferă configurația necesară, dar toate acestea sunt transparente pentru serviciul dumneavoastră.

Desigur, nu există niciun glonț de argint, iar această abordare are limitările sale.

  • Autobuzul de date trebuie să fie suportat intern, spre deosebire de bibliotecile terțe.
  • Data-bus crește numărul de interacțiuni între servicii și brokerul de mesaje, ceea ce are ca rezultat o performanță mai scăzută în comparație cu Kafka.
  • Nu totul poate fi ascuns de servicii atât de ușor, nu dorim să duplicăm funcționalitatea KSQL sau Kafka Streams în magistrala de date, așa că uneori trebuie să permitem ca serviciile să meargă direct.

În cazul nostru, avantajele au depășit contra, iar decizia de a acoperi brokerul de mesaje cu un serviciu separat a fost justificată. Pe parcursul anului de funcționare nu am avut accidente sau probleme grave.

PS Mulțumesc iubitei mele, Ekaterina Obalyaeva, pentru pozele cool pentru acest articol. Daca ti-au placut, aici urmează mai multe ilustrații.

Sursa: www.habr.com

Cumpărați găzduire de încredere pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără găzduire web fiabilă cu protecție DDoS, servere VPS VDS | ProHoster