RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată

В ultimul articol ne-am uitat la clustering RabbitMQ pentru toleranță la erori și disponibilitate ridicată. Acum să pătrundem adânc în Apache Kafka.

Aici unitatea de replicare este partiția. Fiecare subiect are una sau mai multe secțiuni. Fiecare secțiune are un lider cu sau fără adepți. Când creați un subiect, specificați numărul de partiții și coeficientul de replicare. Valoarea obișnuită este 3, ceea ce înseamnă trei replici: un lider și doi adepți.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 1. Patru secțiuni sunt distribuite între trei brokeri

Toate cererile de citire și scriere merg către lider. Adepții trimit periodic cereri liderului pentru a primi cele mai recente mesaje. Consumatorii nu apelează niciodată la adepți; aceștia din urmă există doar pentru redundanță și toleranță la erori.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată

Eșecul partiției

Când un broker eșuează, liderii mai multor secțiuni eșuează adesea. În fiecare dintre ele, un adept dintr-un alt nod devine lider. De fapt, nu este întotdeauna cazul, deoarece factorul de sincronizare influențează și: dacă există adepți sincronizați, iar dacă nu, atunci dacă este permisă trecerea la o replică nesincronizată. Dar să nu complicăm lucrurile deocamdată.

Brokerul 3 părăsește rețeaua și un nou lider este ales pentru secțiunea 2 la brokerul 2.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 2. Brokerul 3 moare și urmașul său de pe brokerul 2 este ales ca noul lider al partiției 2

Apoi, brokerul 1 pleacă și secțiunea 1 își pierde și liderul, al cărui rol trece la broker 2.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 3. A mai rămas un broker. Toți liderii sunt pe un broker cu redundanță zero

Când brokerul 1 revine online, adaugă patru adepți, oferind o anumită redundanță fiecărei partiții. Dar toți liderii au rămas pe brokerul 2.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 4. Liderii rămân pe broker 2

Când apare brokerul 3, ne întoarcem la trei replici per partiție. Dar toți liderii sunt încă pe brokerul 2.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 5. Plasarea dezechilibrată a liderilor după restaurarea brokerilor 1 și 3

Kafka are un instrument pentru o mai bună reechilibrare a liderilor decât RabbitMQ. Acolo, a trebuit să utilizați un plugin sau un script terță parte care a schimbat politicile de migrare a nodului principal prin reducerea redundanței în timpul migrării. În plus, pentru cozile mari a trebuit să acceptăm indisponibilitatea în timpul sincronizării.

Kafka are conceptul de „replici preferate” pentru rolul de lider. Când sunt create partiții de subiecte, Kafka încearcă să distribuie liderii uniform între noduri și marchează primii lideri ca fiind preferați. De-a lungul timpului, din cauza repornirilor serverului, eșecurilor și defecțiunilor conectivității, liderii pot ajunge pe alte noduri, ca în cazul extrem descris mai sus.

Pentru a remedia acest lucru, Kafka oferă două opțiuni:

  • Opțiune auto.leader.rebalance.enable=adevărat permite nodului controler să reatribuie automat liderii înapoi la replicile preferate și, prin urmare, să restabilească distribuția uniformă.
  • Administratorul poate rula scriptul kafka-preferred-replica-election.sh pentru reatribuire manuală.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 6. Replici după reechilibrare

Aceasta a fost o versiune simplificată a eșecului, dar realitatea este mai complexă, deși nu este nimic prea complicat aici. Totul se rezumă la replici sincronizate (Replicas In-Sync, ISR).

Replica sincronizate (ISR)

Un ISR este un set de replici ale unei partiții care este considerată „sincronizată” (în sincronizare). Există un lider, dar s-ar putea să nu existe adepți. Un follower este considerat sincronizat dacă a făcut copii exacte ale tuturor mesajelor liderului înainte de expirarea intervalului replica.lag.time.max.ms.

Un urmăritor este eliminat din setul ISR dacă:

  • nu a făcut o cerere de selectare pentru interval replica.lag.time.max.ms (presupus mort)
  • nu a reusit sa se actualizeze in timpul intervalului replica.lag.time.max.ms (considerat lent)

Adepții fac cereri de eșantionare în interval replica.fetch.wait.max.ms, care este implicit la 500 ms.

Pentru a explica clar scopul ISR, trebuie să ne uităm la confirmările de la producător și la unele scenarii de eșec. Producătorii pot alege când brokerul trimite confirmarea:

  • acks=0, confirmarea nu este trimisă
  • acks=1, confirmarea este trimisă după ce liderul a scris un mesaj în jurnalul său local
  • acks=all, confirmarea este trimisă după ce toate replicile din ISR au scris mesajul în jurnalele locale

În terminologia Kafka, dacă ISR a salvat un mesaj, acesta este „angajat”. Acks=all este cea mai sigură opțiune, dar adaugă și o întârziere suplimentară. Să ne uităm la două exemple de eșec și la modul în care diferitele opțiuni „ack” interacționează cu conceptul ISR.

Acks=1 și ISR

În acest exemplu, vom vedea că dacă liderul nu așteaptă ca fiecare mesaj de la toți adepții să fie salvat, atunci pierderea datelor este posibilă dacă liderul eșuează. Navigarea către un urmăritor nesincronizat poate fi activată sau dezactivată prin setare necurat.lider.elegere.activare.

În acest exemplu, producătorul are valoarea acks=1. Secțiunea este distribuită în toți cei trei brokeri. Broker 3 este în urmă, s-a sincronizat cu liderul în urmă cu opt secunde și acum este cu 7456 de mesaje în urmă. Brokerul 1 era cu doar o secundă în urmă. Producătorul nostru trimite un mesaj și primește rapid un ack înapoi, fără suprasolicitarea adepților lenți sau morți pe care liderul nu îi așteaptă.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 7. ISR cu trei replici

Brokerul 2 eșuează și producătorul primește o eroare de conectare. După ce conducerea trece la broker 1, pierdem 123 de mesaje. Adeptul de pe brokerul 1 făcea parte din ISR, dar nu era pe deplin sincronizat cu liderul când acesta a căzut.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 8. Mesajele se pierd atunci când se blochează

În configurație bootstrap.servere Producătorul are mai mulți brokeri listați și poate întreba un alt broker care este noul lider de secție. Apoi stabilește o conexiune cu brokerul 1 și continuă să trimită mesaje.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 9. Trimiterea mesajelor se reia după o scurtă pauză

Brokerul 3 este și mai în urmă. Face cereri de preluare, dar nu se poate sincroniza. Acest lucru se poate datora conexiunii lente la rețea între brokeri, probleme de stocare etc. Este eliminat din ISR. Acum ISR constă dintr-o replică - liderul! Producătorul continuă să trimită mesaje și să primească confirmări.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 10. Adeptul de pe broker 3 este eliminat din ISR

Brokerul 1 scade, iar rolul de conducere revine brokerului 3 cu pierderea a 15286 de mesaje! Producătorul primește un mesaj de eroare de conexiune. Trecerea la un lider în afara ISR a fost posibilă doar datorită setării unclean.leader.election.enable=adevărat. Dacă este instalat în fals, atunci tranziția nu ar avea loc și toate cererile de citire și scriere ar fi respinse. În acest caz, așteptăm ca brokerul 1 să revină cu datele sale intacte în replică, care va prelua din nou conducerea.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 11. Brokerul 1 cade. Când apare o eroare, un număr mare de mesaje se pierd

Producătorul stabilește o legătură cu ultimul broker și vede că acum este liderul secției. El începe să trimită mesaje către broker 3.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 12. După o scurtă pauză, mesajele sunt trimise din nou la secțiunea 0

Am văzut că, în afară de scurtele întreruperi pentru a stabili noi conexiuni și a căuta un nou lider, producătorul trimitea constant mesaje. Această configurație asigură disponibilitatea în detrimentul consecvenței (securitatea datelor). Kafka a pierdut mii de mesaje, dar a continuat să accepte noi scrieri.

Acks=toate și ISR

Să repetăm ​​acest scenariu din nou, dar cu acks=toate. Broker 3 are o latență medie de patru secunde. Producătorul trimite un mesaj cu acks=toate, iar acum nu primește un răspuns rapid. Liderul așteaptă ca mesajul să fie salvat de toate replicile din ISR.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 13. ISR cu trei replici. Unul este lent, ceea ce duce la întârzieri de înregistrare

După patru secunde de întârziere suplimentară, brokerul 2 trimite un ack. Toate replicile sunt acum complet actualizate.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 14. Toate replicile salvează mesaje și trimit confirmare

Brokerul 3 rămâne acum mai în urmă și este eliminat din ISR. Latența este redusă semnificativ deoarece nu mai există replici lente în ISR. Brokerul 2 așteaptă acum doar brokerul 1 și are un lag mediu de 500 ms.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 15. Replica de pe broker 3 este eliminată din ISR

Apoi, brokerul 2 cade și conducerea trece la brokerul 1 fără a pierde mesajele.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 16. Brokerul 2 cade

Producătorul găsește un nou lider și începe să-i trimită mesaje. Latența este redusă și mai mult, deoarece ISR-ul constă acum dintr-o replică! Prin urmare opțiunea acks=toate nu adaugă redundanță.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 17. Replica pe broker 1 preia conducerea fără a pierde mesaje

Apoi, brokerul 1 se blochează și avantajul merge către brokerul 3 cu o pierdere de 14238 de mesaje!

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 18. Brokerul 1 moare și tranziția de conducere cu o setare necurată are ca rezultat pierderi mari de date

Nu am putut instala opțiunea necurat.lider.elegere.activare în sens adevărat. Implicit este egal cu fals. Setări acks=toate с unclean.leader.election.enable=adevărat oferă accesibilitate cu o anumită securitate suplimentară a datelor. Dar după cum puteți vedea, încă putem pierde mesaje.

Dar dacă dorim să creștem securitatea datelor? Poți să pui unclean.leader.election.enable = fals, dar acest lucru nu ne va proteja neapărat de pierderea datelor. Dacă liderul a căzut greu și a luat datele cu el, atunci mesajele se pierd în continuare, plus disponibilitatea se pierde până când administratorul restabilește situația.

Este mai bine să vă asigurați că toate mesajele sunt redundante și, în caz contrar, renunțați la înregistrare. Apoi, cel puțin din punctul de vedere al brokerului, pierderea datelor este posibilă doar în cazul a două sau mai multe erori simultane.

Acks=all, min.insync.replicas și ISR

Cu configurarea subiectului min.insync.replicas Creștem nivelul de securitate a datelor. Să trecem din nou prin ultima parte a scenariului precedent, dar de data aceasta cu min.insync.replicas=2.

Deci, brokerul 2 are un lider replica, iar adeptul de pe brokerul 3 este eliminat din ISR.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 19. ISR din două replici

Brokerul 2 cade și conducerea trece la broker 1 fără pierderea mesajelor. Dar acum ISR constă dintr-o singură replică. Acest lucru nu întrunește numărul minim pentru a primi înregistrări și, prin urmare, brokerul răspunde la încercarea de scriere cu o eroare NotEnoughReplicas.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 20. Numărul de ISR este cu unul mai mic decât este specificat în min.insync.replicas

Această configurație sacrifică disponibilitatea pentru consecvență. Înainte de a confirma un mesaj, ne asigurăm că acesta este scris pe cel puțin două replici. Acest lucru oferă producătorului mult mai multă încredere. Aici, pierderea mesajului este posibilă numai dacă două replici eșuează simultan într-un interval scurt, până când mesajul este replicat unui adept suplimentar, ceea ce este puțin probabil. Dar dacă ești super paranoic, poți seta factorul de replicare la 5 și min.insync.replicas până la 3. Aici trebuie să cadă trei brokeri în același timp pentru a pierde recordul! Desigur, plătiți pentru această fiabilitate în latență suplimentară.

Când accesibilitatea este necesară pentru securitatea datelor

Ca în caz cu RabbitMQ, uneori accesibilitatea este necesară pentru securitatea datelor. Iată la ce trebuie să te gândești:

  • Poate editorul pur și simplu să returneze o eroare și să solicite serviciului din amonte sau utilizatorului să încerce din nou mai târziu?
  • Poate editorul să salveze mesajul local sau într-o bază de date pentru a încerca din nou mai târziu?

Dacă răspunsul este nu, atunci optimizarea disponibilității îmbunătățește securitatea datelor. Veți pierde mai puține date dacă alegeți disponibilitatea în loc să nu înregistrați. Astfel, totul se rezumă la găsirea unui echilibru, iar decizia depinde de situația specifică.

Semnificația ISR

Suita ISR vă permite să alegeți echilibrul optim între securitatea datelor și latență. De exemplu, asigurați disponibilitatea în cazul eșecului majorității replicilor, minimizând impactul replicilor moarte sau lente în ceea ce privește latența.

Noi înșine alegem sensul replica.lag.time.max.ms conform nevoilor tale. În esență, acest parametru înseamnă cât de multă întârziere suntem dispuși să acceptăm când acks=toate. Valoarea implicită este de zece secunde. Dacă acest lucru este prea lung pentru tine, îl poți reduce. Apoi, frecvența modificărilor în ISR va crește, deoarece adepții vor fi eliminați și adăugați mai des.

RabbitMQ este pur și simplu un set de oglinzi care trebuie replicat. Oglinzile lente introduc o latență suplimentară, iar oglinzile moarte pot aștepta până când pachetele care verifică disponibilitatea fiecărui nod (ticul net) să răspundă. ISR este o modalitate interesantă de a evita aceste probleme de latență. Dar riscăm să pierdem redundanța, deoarece ISR nu poate decât să se micșoreze la lider. Pentru a evita acest risc, utilizați setarea min.insync.replicas.

Garanția conexiunii clientului

În setări bootstrap.servere producătorul și consumatorul pot specifica mai mulți brokeri pentru conectarea clienților. Ideea este că atunci când un nod scade, rămân mai multe altele de rezervă cu care clientul poate deschide o conexiune. Aceștia nu sunt neapărat lideri de secție, ci pur și simplu o trambulină pentru încărcarea inițială. Clientul îi poate întreba ce nod găzduiește liderul partiției de citire/scriere.

În RabbitMQ, clienții se pot conecta la orice nod, iar rutarea internă trimite cererea acolo unde trebuie să ajungă. Aceasta înseamnă că puteți instala un echilibrator de încărcare în fața RabbitMQ. Kafka solicită clienților să se conecteze la nodul care găzduiește liderul partiției corespunzător. Într-o astfel de situație, nu puteți instala un echilibrator de încărcare. Listă bootstrap.servere Este esențial ca clienții să poată accesa și să găsească nodurile corecte după o eroare.

Arhitectura de consens Kafka

Până acum, nu am luat în considerare modul în care clusterul află despre căderea brokerului și cum este ales un nou lider. Pentru a înțelege cum funcționează Kafka cu partițiile de rețea, mai întâi trebuie să înțelegeți arhitectura de consens.

Fiecare cluster Kafka este implementat împreună cu un cluster Zookeeper, care este un serviciu de consens distribuit, care permite sistemului să ajungă la un consens cu privire la o anumită stare, dând prioritate consistenței față de disponibilitate. Consimțământul majorității nodurilor Zookeeper este necesar pentru a aproba operațiunile de citire și scriere.

Zookeeper stochează starea cluster-ului:

  • Lista de subiecte, secțiuni, configurație, replici lider curente, replici preferate.
  • Membrii clusterului. Fiecare broker dă ping grupului Zookeeper. Dacă nu primește un ping într-o anumită perioadă de timp, atunci Zookeeper înregistrează brokerul ca indisponibil.
  • Selectarea nodurilor principale și de rezervă pentru controler.

Nodul controler este unul dintre brokerii Kafka care este responsabil pentru alegerea liderilor replica. Zookeeper trimite notificări controlorului despre apartenența la cluster și modificările subiectelor, iar controlorul trebuie să acționeze asupra acestor modificări.

De exemplu, să luăm un subiect nou cu zece partiții și un factor de replicare de 3. Controlorul trebuie să aleagă un lider pentru fiecare partiție, încercând să distribuie în mod optim liderii între brokeri.

Pentru fiecare controler de secțiune:

  • actualizează informațiile din Zookeeper despre ISR și lider;
  • Trimite un LeaderAndISRCommand fiecărui broker care găzduiește o replică a acestei partiții, informând brokerii despre ISR și lider.

Când un broker cu un lider cade, Zookeeper trimite o notificare controlorului și alege un nou lider. Din nou, controlorul actualizează mai întâi Zookeeper și apoi trimite o comandă fiecărui broker, informându-i despre schimbarea conducerii.

Fiecare lider este responsabil pentru recrutarea ISR. Setări replica.lag.time.max.ms determină cine va intra acolo. Când ISR se schimbă, liderul transmite noi informații către Zookeeper.

Zookeeper este întotdeauna informat cu privire la orice modificare, astfel încât, în cazul unui eșec, managementul trece fără probleme la un nou lider.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 21. Consensul Kafka

Protocol de replicare

Înțelegerea detaliilor replicării vă ajută să înțelegeți mai bine scenariile potențiale de pierdere a datelor.

Interogări de eșantionare, decalaj de sfârșit al jurnalului (LEO) și marcaj de apă mare (HW)

Am considerat că urmăritorii trimit periodic solicitări de preluare către lider. Intervalul implicit este de 500 ms. Acest lucru diferă de RabbitMQ prin faptul că în RabbitMQ replicarea nu este inițiată de oglinda cozii, ci de către master. Stăpânul împinge schimbări în oglinzi.

Liderul și toți adepții salvează eticheta Log End Offset (LEO) și Highwater (HW). Marcajul LEO stochează offset-ul ultimului mesaj în replica locală, iar HW-ul păstrează offset-ul ultimului commit. Amintiți-vă că pentru starea de comitere, mesajul trebuie să fie păstrat în toate replicile ISR. Aceasta înseamnă că LEO este de obicei ușor înaintea HW.

Când liderul primește un mesaj, acesta îl stochează local. Adeptul face o cerere de preluare prin transmiterea lui LEO. Liderul trimite apoi un lot de mesaje pornind de la acest LEO și, de asemenea, transmite HW-ul curent. Când liderul primește informații că toate replicile au stocat mesajul la offset-ul dat, acesta mută marcajul HW. Numai liderul poate muta HW-ul și astfel toți adepții vor cunoaște valoarea curentă în răspunsurile la cererea lor. Aceasta înseamnă că adepții pot rămâne în urmă liderului atât în ​​ceea ce privește mesajul, cât și cunoștințele HW. Consumatorii primesc mesaje numai până la HW-ul curent.

Rețineți că „persistat” înseamnă scris în memorie, nu pe disc. Pentru performanță, Kafka se sincronizează pe disc la un anumit interval. RabbitMQ are și un astfel de interval, dar va trimite o confirmare către editor numai după ce masterul și toate oglinzile au scris mesajul pe disc. Dezvoltatorii Kafka, din motive de performanță, au decis să trimită un ack de îndată ce mesajul este scris în memorie. Kafka pariază că redundanța compensează riscul de a stoca pentru scurt timp mesajele confirmate doar în memorie.

Eșecul liderului

Când un lider cade, Zookeeper îl anunță pe controlor și selectează o nouă replică a liderului. Noul lider stabilește un nou marcaj HW conform LEO-ului său. Adepții primesc apoi informații despre noul lider. În funcție de versiunea lui Kafka, adeptul va alege unul dintre cele două scenarii:

  1. Acesta va trunchia jurnalul local la un HW cunoscut și va trimite o solicitare noului lider pentru mesaje după acest marcaj.
  2. Va trimite o solicitare liderului pentru a afla HW-ul la momentul în care a fost ales lider, apoi va trunchia jurnalul la acest offset. Apoi va începe să facă cereri periodice de preluare începând de la acest offset.

Este posibil ca un utilizator să fie nevoie să trunchieze jurnalul din următoarele motive:

  • Când un lider eșuează, primul adept din setul ISR înregistrat la Zookeeper câștigă alegerile și devine lider. Toți adepții de pe ISR, deși considerați „sincronizați”, este posibil să nu fi primit copii ale tuturor mesajelor de la fostul lider. Este cu totul posibil ca urmăritorul prezentat să nu aibă cea mai actualizată copie. Kafka se asigură că nu există divergențe între replici. Astfel, pentru a evita discrepanțe, fiecare adept trebuie să-și trunnească jurnalul la valoarea HW a noului lider la momentul alegerii sale. Acesta este un alt motiv pentru care setarea acks=toate atât de important pentru consistență.
  • Mesajele sunt scrise periodic pe disc. Dacă toate nodurile cluster eșuează în același timp, atunci replicile cu diferite decalaje vor fi stocate pe discuri. Este posibil ca atunci când brokerii revin online, noul lider care este ales să fie în spatele adepților săi, deoarece a fost salvat pe disc înaintea celorlalți.

Reuniunea cu clusterul

Când se reunesc în cluster, replicile fac la fel ca atunci când un lider eșuează: verifică replica liderului și își trunchiază jurnalul în HW-ul acestuia (la momentul alegerii). În comparație, RabbitMQ tratează în mod egal nodurile reunite ca fiind complet noi. În ambele cazuri, brokerul renunță la orice stare existentă. Dacă se folosește sincronizarea automată, atunci masterul trebuie să reproducă absolut tot conținutul actual în noua oglindă într-o metodă „lasă întreaga lume să aștepte”. Masterul nu acceptă operațiuni de citire sau scriere în timpul acestei operațiuni. Această abordare creează probleme în cozile mari.

Kafka este un jurnal distribuit și, în general, stochează mai multe mesaje decât o coadă RabbitMQ, unde datele sunt eliminate din coadă după ce sunt citite. Cozile active ar trebui să rămână relativ mici. Dar Kafka este un jurnal cu propria sa politică de păstrare, care poate seta o perioadă de zile sau săptămâni. Abordarea blocării cozii și a sincronizării complete este absolut inacceptabilă pentru un jurnal distribuit. În schimb, adepții Kafka pur și simplu își trunchiază jurnalul în HW-ul liderului (la momentul alegerii acestuia) dacă copia lor este înaintea liderului. În cazul cel mai probabil, când adeptul este în urmă, pur și simplu începe să facă cereri de preluare începând cu LEO-ul său curent.

Adepții noi sau reînscriși încep în afara ISR și nu participă la comiteri. Pur și simplu lucrează alături de grup, primind mesaje cât de repede pot până când îl ajung din urmă pe lider și intră în ISR. Nu există blocare și nu este nevoie să vă aruncați toate datele.

Pierderea conectivității

Kafka are mai multe componente decât RabbitMQ, deci are un set mai complex de comportamente atunci când clusterul devine deconectat. Dar Kafka a fost conceput inițial pentru clustere, așa că soluțiile sunt foarte bine gândite.

Mai jos sunt câteva scenarii de defecțiune a conectivității:

  • Scenariul 1: adeptul nu-l vede pe lider, dar tot îl vede pe Zookeeper.
  • Scenariul 2: Liderul nu vede niciun urmăritor, dar încă îl vede pe Zookeeper.
  • Scenariul 3: adeptul îl vede pe lider, dar nu îl vede pe Zookeeper.
  • Scenariul 4: Liderul vede adepții, dar nu îl vede pe Zookeeper.
  • Scenariul 5: adeptul este complet separat atât de celelalte noduri Kafka, cât și de Zookeeper.
  • Scenariul 6: Liderul este complet separat atât de celelalte noduri Kafka, cât și de Zookeeper.
  • Scenariul 7: Nodul controler Kafka nu poate vedea un alt nod Kafka.
  • Scenariul 8: Controlerul Kafka nu îl vede pe Zookeeper.

Fiecare scenariu are propriul său comportament.

Scenariul 1: Adeptul nu vede liderul, dar tot îl vede pe Zookeeper

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 22. Scenariul 1: ISR a trei replici

Eșecul de conectivitate separă brokerul 3 de brokerii 1 și 2, dar nu și de Zookeeper. Brokerul 3 nu mai poate trimite cereri de preluare. După ce timpul a trecut replica.lag.time.max.ms este eliminat din ISR și nu participă la comiterea mesajelor. Odată ce conectivitatea este restabilită, va relua cererile de preluare și se va alătura ISR-ului atunci când îl va ajunge din urmă pe lider. Zookeeper va continua să primească ping-uri și va presupune că brokerul este în viață și sănătos.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 23. Scenariul 1: Brokerul este eliminat din ISR dacă nu se primește nicio cerere de preluare de la acesta în intervalul replica.lag.time.max.ms

Nu există suspensie de creier divizat sau de noduri ca în RabbitMQ. În schimb, redundanța este redusă.

Scenariul 2: Liderul nu vede niciun urmăritor, dar încă îl vede pe Zookeeper

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 24. Scenariul 2. Lider și doi adepți

O defecțiune a conectivității la rețea îl separă pe lider de adepți, dar brokerul îl poate vedea în continuare pe Zookeeper. Ca și în primul scenariu, ISR-ul se micșorează, dar de data aceasta doar către lider, deoarece toți adepții nu mai trimit solicitări de preluare. Din nou, nu există o divizare logică. În schimb, există o pierdere a redundanței pentru mesajele noi până la restabilirea conectivității. Zookeeper continuă să primească ping-uri și crede că brokerul este în viață și sănătos.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 25. Scenariul 2. ISR s-a redus doar la lider

Scenariul 3. Adeptul îl vede pe lider, dar nu îl vede pe Zookeeper

Adeptul este separat de Zookeeper, dar nu de broker cu liderul. Drept urmare, adeptul continuă să facă cereri de preluare și să fie membru al ISR. Zookeeper nu mai primește ping-uri și înregistrează o prăbușire a brokerului, dar deoarece este doar un follower, nu există consecințe după recuperare.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 26. Scenariul 3: adeptul continuă să trimită cereri de preluare liderului

Scenariul 4. Liderul vede urmăritori, dar nu vede Zookeeper

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 27. Scenariul 4. Lider și doi adepți

Liderul este separat de Zookeeper, dar nu de brokerii cu adepți.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 28. Scenariul 4: Liderul izolat de Zookeeper

După ceva timp, Zookeeper va înregistra o defecțiune a brokerului și va notifica controlorul despre aceasta. El va alege un nou lider printre adepții săi. Cu toate acestea, liderul inițial va continua să creadă că este liderul și va continua să accepte intrări de la acks=1. Adepții nu îi mai trimit cereri de preluare, așa că le va considera morți și va încerca să reducă ISR-ul la sine. Dar, deoarece nu are o conexiune cu Zookeeper, nu va putea face acest lucru și, în acel moment, va refuza să accepte alte intrări.

Mesaje acks=toate nu va primi o confirmare deoarece ISR-ul pornește mai întâi toate replicile, iar mesajele nu ajung la ele. Când liderul inițial încearcă să le elimine din ISR, nu va putea face acest lucru și nu va mai accepta niciun mesaj.

Clienții observă în curând schimbarea liderului și încep să trimită înregistrări către noul server. Odată ce rețeaua este restaurată, liderul inițial vede că nu mai este lider și își trunchiază jurnalul la valoarea HW pe care o avea noul lider la momentul eșecului pentru a evita divergența jurnalului. Apoi va începe să trimită cereri de preluare noului lider. Toate înregistrările de la liderul original care nu sunt replicate noului lider se pierd. Adică, mesajele care nu au fost recunoscute de liderul inițial în acele câteva secunde când doi lideri lucrau se vor pierde.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 29. Scenariul 4. Liderul de pe brokerul 1 devine un adept după ce rețeaua este restaurată

Scenariul 5: adeptul este complet separat atât de celelalte noduri Kafka, cât și de Zookeeper

Adeptul este complet izolat atât de celelalte noduri Kafka, cât și de Zookeeper. Pur și simplu se îndepărtează din ISR până când rețeaua este restabilită și apoi îi ajunge din urmă pe ceilalți.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 30. Scenariul 5: adeptul izolat este eliminat din ISR

Scenariul 6: Liderul este complet separat atât de celelalte noduri Kafka, cât și de Zookeeper

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 31. Scenariul 6. Lider și doi adepți

Liderul este complet izolat de adepții săi, controlor și Zookeeper. Pentru o perioadă scurtă va continua să accepte intrări de la acks=1.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 32. Scenariul 6: Izolarea liderului de alte noduri Kafka și Zookeeper

Nu a primit cereri după expirare replica.lag.time.max.ms, va încerca să micșoreze ISR-ul la sine, dar nu va putea face acest lucru pentru că nu există comunicare cu Zookeeper, apoi va înceta să accepte scrieri.

Între timp, Zookeeper va marca brokerul izolat ca mort, iar controlorul va alege un nou lider.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 33. Scenariul 6. Doi lideri

Liderul original poate accepta intrări pentru câteva secunde, dar apoi încetează să accepte orice mesaj. Clienții sunt actualizați la fiecare 60 de secunde cu cele mai recente metadate. Ei vor fi informați despre schimbarea liderului și vor începe să trimită intrări noului lider.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 34. Scenariul 6: Producătorii trec la un nou lider

Toate intrările confirmate făcute de liderul inițial de la pierderea conectivității se vor pierde. Odată ce rețeaua este restaurată, liderul original va descoperi prin Zookeeper că nu mai este liderul. Apoi își va trunchia jurnalul în HW-ul noului lider în momentul alegerilor și va începe să trimită cereri ca adept.

RabbitMQ vs Kafka: toleranță la erori și disponibilitate ridicată
Orez. 35. Scenariul 6: Liderul inițial devine un adept după ce conexiunea la rețea este restabilită

În această situație, separarea logică poate apărea pentru o perioadă scurtă, dar numai dacă acks=1 и min.insync.replicas de asemenea 1. Separarea logică se termină automat fie după ce rețeaua este restabilită, când liderul inițial își dă seama că nu mai este lider, fie când toți clienții își dau seama că liderul s-a schimbat și încep să scrie noului lider - oricare se întâmplă mai întâi. În orice caz, unele mesaje se vor pierde, dar numai cu acks=1.

Există o altă variantă a acestui scenariu în care, chiar înainte de divizarea rețelei, adepții au rămas în urmă, iar liderul a comprimat ISR-ul doar pentru el însuși. Apoi devine izolat din cauza pierderii conectivității. Este ales un nou lider, dar liderul inițial continuă să accepte înscrieri, chiar și acks=toate, pentru că nu este nimeni altcineva în ISR în afară de el. Aceste înregistrări se vor pierde odată ce rețeaua este restaurată. Singura modalitate de a evita această opțiune este min.insync.replicas = 2.

Scenariul 7: Nodul de control Kafka nu poate vedea un alt nod Kafka

În general, odată ce conexiunea cu un nod Kafka este pierdută, controlerul nu va putea transmite acestuia nicio informație privind schimbarea liderului. În cel mai rău caz, acest lucru va duce la o separare logică pe termen scurt, ca în scenariul 6. De cele mai multe ori, brokerul pur și simplu nu va deveni un candidat pentru conducere dacă acesta din urmă eșuează.

Scenariul 8: Controlerul Kafka nu îl vede pe Zookeeper

Zookeeper nu va primi un ping de la controlerul căzut și va selecta un nou nod Kafka ca controler. Controlerul original poate continua să se prezinte ca atare, dar nu primește notificări de la Zookeeper, așa că nu va avea nicio sarcină de îndeplinit. Odată restabilită rețeaua, el își va da seama că nu mai este controler, ci a devenit un nod Kafka obișnuit.

Concluzii din scenarii

Vedem că pierderea conectivității adepților nu are ca rezultat pierderea mesajelor, ci pur și simplu reduce temporar redundanța până când rețeaua este restaurată. Acest lucru, desigur, poate duce la pierderea datelor dacă unul sau mai multe noduri sunt pierdute.

Dacă liderul este separat de Zookeeper din cauza unei pierderi de conectivitate, acest lucru ar putea duce la pierderea mesajelor de la acks=1. Lipsa de comunicare cu Zookeeper provoacă o scurtă divizare logică cu cei doi lideri. Această problemă este rezolvată de parametru acks=toate.

Parametru min.insync.replicas în două sau mai multe replici oferă o asigurare suplimentară că astfel de scenarii pe termen scurt nu vor avea ca rezultat mesaje pierdute, ca în Scenariul 6.

Rezumatul mesajelor pierdute

Să enumerăm toate modalitățile prin care puteți pierde date în Kafka:

  • Orice eșec de lider dacă mesajele au fost confirmate folosind acks=1
  • Orice tranziție necurată a conducerii, adică la un adept din afara ISR, chiar și cu acks=toate
  • Izolarea liderului de Zookeeper dacă mesajele au fost confirmate folosind acks=1
  • Izolarea completă a liderului care a redus deja grupul ISR la el însuși. Toate mesajele vor fi pierdute, chiar și acks=toate. Acest lucru este adevărat numai dacă min.insync.replicas=1.
  • Eșecuri simultane ale tuturor nodurilor de partiție. Deoarece mesajele sunt confirmate din memorie, este posibil ca unele să nu fie încă scrise pe disc. După repornirea serverelor, unele mesaje pot lipsi.

Tranzițiile impure de conducere pot fi evitate fie prin interzicerea lor, fie prin asigurarea a cel puțin două concedieri. Cea mai durabilă configurație este o combinație acks=toate и min.insync.replicas mai mult de 1.

Comparație directă a fiabilității RabbitMQ și Kafka

Pentru a asigura fiabilitatea și disponibilitatea ridicată, ambele platforme implementează un sistem de replicare primar și secundar. Cu toate acestea, RabbitMQ are călcâiul lui Ahile. La reconectare după o eroare, nodurile își renunță datele și sincronizarea este blocată. Acest dublu zgomot pune sub semnul întrebării longevitatea cozilor mari în RabbitMQ. Va trebui să acceptați fie o redundanță redusă, fie timpi lungi de blocare. Reducerea redundanței crește riscul pierderii masive de date. Dar dacă cozile sunt mici, atunci, de dragul redundanței, perioadele scurte de indisponibilitate (câteva secunde) pot fi tratate folosind încercări repetate de conectare.

Kafka nu are această problemă. Îndepărtează datele numai din punctul de divergență dintre lider și urmaș. Toate datele partajate sunt salvate. În plus, replicarea nu blochează sistemul. Liderul continuă să accepte postări în timp ce noul follower ajunge din urmă, așa că pentru devops, alăturarea sau reîncărcarea grupului devine o sarcină trivială. Desigur, există încă probleme precum lățimea de bandă a rețelei în timpul replicării. Dacă adăugați mai mulți urmăritori în același timp, este posibil să întâlniți o limită de lățime de bandă.

RabbitMQ este superior lui Kafka în ceea ce privește fiabilitatea atunci când mai multe servere dintr-un cluster eșuează în același timp. După cum am spus deja, RabbitMQ trimite o confirmare editorului numai după ce mesajul este scris pe disc de către master și toate oglinzile. Dar acest lucru adaugă o latență suplimentară din două motive:

  • fsync la fiecare câteva sute de milisecunde
  • Eșecul oglinzii poate fi observat doar după ce durata de viață a pachetelor care verifică disponibilitatea fiecărui nod (net tick) a expirat. Dacă oglinda încetinește sau cade, aceasta adaugă o întârziere.

Pariul lui Kafka este că, dacă un mesaj este stocat pe mai multe noduri, acesta poate confirma mesajele imediat ce ajung în memorie. Din acest motiv, există riscul de a pierde mesaje de orice tip (chiar acks=toate, min.insync.replicas=2) în caz de defecţiune simultană.

În general, Kafka prezintă performanțe software mai bune și este proiectat de la zero pentru clustere. Numărul de urmăritori poate fi crescut la 11 dacă este necesar pentru fiabilitate. Factorul de replicare 5 și numărul minim de replici în sincronizare min.insync.replicas=3 va face pierderea mesajului un eveniment foarte rar. Dacă infrastructura dumneavoastră poate suporta acest raport de replicare și acest nivel de redundanță, atunci puteți alege această opțiune.

Gruparea RabbitMQ este bună pentru cozile mici. Dar chiar și cozile mici pot crește rapid atunci când există trafic intens. Odată ce cozile devin mari, va trebui să faceți alegeri dificile între disponibilitate și fiabilitate. Gruparea RabbitMQ este cea mai potrivită pentru situațiile non-tipice în care beneficiile flexibilității RabbitMQ depășesc orice dezavantaje ale grupării sale.

Un antidot împotriva vulnerabilității RabbitMQ la cozile mari este împărțirea lor în multe cozi mai mici. Dacă nu aveți nevoie de comanda completă a întregii cozi, ci doar de mesajele relevante (de exemplu, mesaje de la un anumit client) sau nu comandați absolut nimic, atunci această opțiune este acceptabilă: uitați-vă la proiectul meu Reechilibrator pentru a împărți coada (proiectul este încă într-un stadiu incipient).

În cele din urmă, nu uitați de o serie de erori în mecanismele de grupare și replicare ale RabbitMQ și Kafka. De-a lungul timpului, sistemele au devenit mai mature și mai stabile, dar niciun mesaj nu va fi vreodată 100% protejat de pierdere! În plus, în centrele de date au loc accidente de mare amploare!

Dacă am omis ceva, am făcut o greșeală sau nu sunteți de acord cu oricare dintre puncte, nu ezitați să scrieți un comentariu sau să mă contactați.

Sunt adesea întrebat: „Ce să aleg, Kafka sau RabbitMQ?”, „Ce platformă este mai bună?”. Adevărul este că depinde foarte mult de situația ta, de experiența actuală etc. Ezit să-mi dau părerea pentru că ar fi o simplificare prea mare să recomand o singură platformă pentru toate cazurile de utilizare și posibilele limitări. Am scris această serie de articole ca să vă puteți forma propria părere.

Vreau să spun că ambele sisteme sunt lideri în acest domeniu. S-ar putea să fiu puțin părtinitor, deoarece, din experiența mea cu proiecte, tind să prețuiesc lucruri precum ordonarea garantată a mesajelor și fiabilitatea.

Văd alte tehnologii cărora le lipsește această fiabilitate și o comandă garantată, apoi mă uit la RabbitMQ și Kafka și îmi dau seama de valoarea incredibilă a ambelor sisteme.

Sursa: www.habr.com

Adauga un comentariu