Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Ce ar putea obliga o companie atât de mare precum Lamoda, cu un proces simplificat și zeci de servicii interconectate, să-și schimbe semnificativ abordarea? Motivația poate fi complet diferită: de la legislativ la dorința de a experimenta inerentă tuturor programatorilor.

Dar asta nu înseamnă că nu poți conta pe beneficii suplimentare. Sergey Zaika vă va spune exact ce puteți câștiga dacă implementați API-ul bazat pe evenimente pe Kafka (fewald). De asemenea, cu siguranță se va vorbi despre mari și descoperiri interesante - experimentul nu se poate descurca fără ele.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Disclaimer: Acest articol se bazează pe materiale de la o întâlnire organizată de Sergey în noiembrie 2018 pe HighLoad++. Experiența live a lui Lamoda de a lucra cu Kafka a atras ascultătorii nu mai puțin decât alte rapoarte despre program. Credem că acesta este un exemplu excelent al faptului că poți și ar trebui să găsești mereu oameni cu gânduri similare, iar organizatorii HighLoad++ vor continua să încerce să creeze o atmosferă propice pentru acest lucru.

Despre proces

Lamoda este o platformă mare de comerț electronic care are propriul centru de contact, serviciu de livrare (și mulți afiliați), un studio foto, un depozit imens și toate acestea rulează pe propriul software. Există zeci de metode de plată, parteneri b2b care pot folosi unele sau toate aceste servicii și doresc să afle informații actualizate despre produsele lor. În plus, Lamoda operează în trei țări în afară de Federația Rusă și acolo totul este puțin diferit. În total, există probabil mai mult de o sută de moduri de a configura o nouă comandă, care trebuie procesată în felul său. Toate acestea funcționează cu ajutorul a zeci de servicii care uneori comunică în moduri neevidente. Există, de asemenea, un sistem central a cărui principală responsabilitate este starea comenzilor. O numim BOB, lucrez cu ea.

Instrument de rambursare cu API-ul bazat pe evenimente

Cuvântul acționat de evenimente este destul de ușurat; puțin mai departe vom defini mai detaliat ce se înțelege prin aceasta. Voi începe cu contextul în care am decis să încercăm abordarea API bazată pe evenimente în Kafka.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

În orice magazin, pe lângă comenzile pentru care clienții plătesc, există momente în care magazinul este obligat să returneze bani pentru că produsul nu i se potrivea clientului. Acesta este un proces relativ scurt: clarificăm informațiile, dacă este necesar, și transferăm banii.

Dar întoarcerea a devenit mai complicată din cauza modificărilor din legislație și a trebuit să implementăm un microserviciu separat pentru aceasta.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Motivația noastră:

  1. Legea FZ-54 - pe scurt, legea impune raportarea la fisc cu privire la fiecare tranzacție monetară, fie ea retur sau chitanță, într-un SLA destul de scurt de câteva minute. Noi, ca companie de comert electronic, desfasuram destul de multe operatiuni. Din punct de vedere tehnic, aceasta înseamnă o nouă responsabilitate (și, prin urmare, un nou serviciu) și îmbunătățiri în toate sistemele implicate.
  2. BOB s-a despărțit este un proiect intern al companiei pentru a scuti BOB de un număr mare de responsabilități non-core și pentru a reduce complexitatea sa generală.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Această diagramă prezintă principalele sisteme Lamoda. Acum majoritatea sunt mai mulți o constelație de 5-10 microservicii în jurul unui monolit în scădere. Ele cresc încet, dar încercăm să le facem mai mici, deoarece desfășurarea fragmentului selectat la mijloc este înfricoșătoare - nu putem permite să cadă. Suntem nevoiți să rezervăm toate schimburile (săgețile) și să luăm în considerare faptul că oricare dintre ele se poate dovedi a fi indisponibil.

BOB are, de asemenea, destul de multe schimburi: sisteme de plată, sisteme de livrare, sisteme de notificare etc.

Din punct de vedere tehnic, BOB este:

  • ~150k linii de cod + ~100k linii de teste;
  • php7.2 + Zend 1 & Symfony Components 3;
  • >100 de API-uri și ~50 de integrări de ieșire;
  • 4 țări cu propria lor logică de afaceri.

Implementarea BOB este costisitoare și dureroasă, cantitatea de cod și problemele pe care le rezolvă este de așa natură încât nimeni nu le poate pune totul în cap. În general, există multe motive pentru a o simplifica.

Procesul de returnare

Inițial, în proces sunt implicate două sisteme: BOB și Payment. Acum mai apar două:

  • Serviciul de Fiscalizare, care se va ocupa de problemele de fiscalizare si de comunicare cu serviciile externe.
  • Instrumentul de rambursare, care conține pur și simplu schimburi noi pentru a nu umfla BOB.

Acum procesul arată astfel:

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

  1. BOB primește o cerere de rambursare.
  2. BOB vorbește despre acest instrument de rambursare.
  3. Instrumentul de rambursare îi spune Payment: „Întoarceți banii”.
  4. Plata returnează banii.
  5. Instrumentul de rambursare și BOB sincronizează stările între ele, pentru că deocamdată ambele au nevoie de el. Nu suntem încă pregătiți să trecem complet la Instrumentul de rambursare, deoarece BOB are o interfață de utilizare, rapoarte pentru contabilitate și, în general, o mulțime de date care nu pot fi transferate atât de ușor. Trebuie să stai pe două scaune.
  6. Cererea de fiscalizare dispare.

Drept urmare, am făcut un fel de autobuz de evenimente pe Kafka - autobuz de evenimente, pe care a început totul. Ura, acum avem un singur punct de eșec (sarcasm).

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Avantajele și dezavantajele sunt destul de evidente. Am făcut un autobuz, ceea ce înseamnă că acum toate serviciile depind de el. Acest lucru simplifică proiectarea, dar introduce un singur punct de defecțiune în sistem. Kafka se va prăbuși, procesul se va opri.

Ce este un API bazat pe evenimente

Un răspuns bun la această întrebare se află în raportul lui Martin Fowler (GOTO 2017) „Multele semnificații ale arhitecturii bazate pe evenimente”.

Pe scurt ce am făcut:

  1. Finalizați toate schimburile asincrone prin stocarea evenimentelor. În loc să informăm fiecare consumator interesat despre o schimbare a stării în rețea, scriem un eveniment despre o schimbare a stării într-o stocare centralizată, iar consumatorii interesați de subiect citesc tot ce apare de acolo.
  2. Evenimentul în acest caz este o notificare (notificări) că ceva s-a schimbat undeva. De exemplu, starea comenzii s-a schimbat. Un consumator care este interesat de unele date care însoțesc schimbarea statutului care nu sunt incluse în notificare poate afla el însuși starea acesteia.
  3. Opțiunea maximă este aprovizionarea cu drepturi depline pentru evenimente, transfer de stat, în care eveniment conține toate informațiile necesare procesării: de unde provine și în ce stare a intrat, cum exact s-au schimbat datele etc. Singura întrebare este fezabilitatea și cantitatea de informații pe care vă puteți permite să o stocați.

Ca parte a lansării Instrumentului de rambursare, am folosit a treia opțiune. Această procesare a evenimentelor simplificată, deoarece nu a fost nevoie de extragerea de informații detaliate, plus a eliminat scenariul în care fiecare nou eveniment generează o explozie de clarificări pentru a obține solicitări de la consumatori.

Serviciu instrument de rambursare nu este încărcat, așa că Kafka există mai mult un gust de stilou decât o necesitate. Nu cred că dacă serviciul de rambursare ar deveni un proiect cu sarcină mare, afacerile ar fi fericite.

Schimb asincron CA ESTE

Pentru schimburile asincrone, departamentul PHP folosește de obicei RabbitMQ. Am colectat datele pentru cerere, am pus-o la coadă, iar consumatorul aceluiași serviciu a citit-o și a trimis-o (sau nu a trimis-o). Pentru API-ul în sine, Lamoda folosește în mod activ Swagger. Proiectăm un API, îl descriem în Swagger și generăm cod de client și server. De asemenea, folosim un JSON RPC 2.0 ușor îmbunătățit.

În unele locuri sunt folosite autobuze ESB, unele trăiesc pe activeMQ, dar, în general, RabbitMQ - standard.

Schimb asincron TO FI

La proiectarea schimbului prin intermediul autobuzului de evenimente, poate fi urmărită o analogie. În mod similar, descriem viitorul schimb de date prin descrierile structurii evenimentelor. Formatul yaml, a trebuit să facem noi înșine generarea codului, generatorul creează DTO-uri conform specificațiilor și învață clienții și serverele să lucreze cu ele. Generația trece în două limbi - golang și php. Acest lucru ajută la menținerea coerentei bibliotecilor. Generatorul este scris în golang, motiv pentru care a primit numele de gogi.

Aprovizionarea cu evenimente pe Kafka este un lucru tipic. Există o soluție din versiunea principală a companiei Kafka Confluent, există nakadi, o soluție de la frații noștri de domeniu Zalando. Al nostru motivația de a începe cu vanilia Kafka - aceasta înseamnă să lăsăm soluția liberă până când decidem în sfârșit dacă o vom folosi peste tot și, de asemenea, să ne lăsăm spațiu de manevră și îmbunătățiri: vrem sprijin pentru JSON RPC 2.0, generatoare pentru două limbi și să vedem ce altceva.

Este ironic că, chiar și într-un caz atât de fericit, când există o afacere aproximativ similară, Zalando, care a făcut o soluție aproximativ similară, nu o putem folosi eficient.

Modelul arhitectural la lansare este următorul: citim direct din Kafka, dar scriem doar prin events-bus. Sunt multe gata de citit în Kafka: brokeri, echilibratori și este mai mult sau mai puțin gata pentru scalare orizontală, am vrut să păstrez asta. Am vrut să finalizăm înregistrarea printr-un singur Gateway, alias Events-bus, și iată de ce.

Evenimente-autobuz

Sau un autobuz de evenimente. Acesta este pur și simplu un gateway http fără stat, care își asumă mai multe roluri importante:

  • Producerea validării — verificăm dacă evenimentele respectă specificațiile noastre.
  • Sistem principal de evenimente, adică acesta este principalul și singurul sistem din companie care răspunde la întrebarea despre ce evenimente cu ce structuri sunt considerate valide. Validarea implică pur și simplu tipuri de date și enumerari pentru a specifica strict conținutul.
  • Funcția hash pentru sharding - structura mesajului Kafka este cheie-valoare și folosind hash-ul cheii se calculează unde se pune.

De ce

Lucrăm într-o companie mare cu un proces simplificat. De ce schimba ceva? Acesta este un experimentși ne așteptăm să obținem mai multe beneficii.

1:n+1 schimburi (unu la mai multe)

Kafka facilitează conectarea noilor consumatori la API.

Să presupunem că aveți un director pe care trebuie să îl țineți actualizat în mai multe sisteme simultan (și în unele noi). Anterior, am inventat un pachet care implementa set-API, iar sistemul principal a fost informat despre adresele consumatorilor. Acum, sistemul principal trimite actualizări ale subiectului și toți cei interesați îl citesc. A apărut un nou sistem - l-am înscris pentru acest subiect. Da, și pachet, dar mai simplu.

În cazul instrumentului de rambursare, care este o bucată de BOB, este convenabil pentru noi să le menținem sincronizate prin Kafka. Plata spune că banii au fost returnați: BOB, RT au aflat despre asta, și-au schimbat statutul, Serviciul de Fiscalizare a aflat despre asta și a emis un cec.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Avem planuri de a crea un Serviciu de Notificări unificat care să informeze clientul despre noutățile privind comanda/retururile sale. Acum această responsabilitate este răspândită între sisteme. Va fi suficient să învățăm Serviciul de notificări să prindă informații relevante de la Kafka și să le răspundă (și să dezactiveze aceste notificări în alte sisteme). Nu vor fi necesare schimburi directe noi.

Bazat pe date

Informațiile dintre sisteme devin transparente - indiferent ce „întreprindere sângeroasă” ai și indiferent cât de plin este acumularea ta. Lamoda are un departament de Data Analytics care colectează date din sisteme și le pune într-o formă reutilizabilă, atât pentru afaceri, cât și pentru sisteme inteligente. Kafka vă permite să le oferiți rapid o mulțime de date și să mențineți acel flux de informații actualizat.

Jurnal de replicare

Mesajele nu dispar după ce au fost citite, ca în RabbitMQ. Când un eveniment conține suficiente informații pentru procesare, avem un istoric al modificărilor recente ale obiectului și, dacă se dorește, capacitatea de a aplica aceste modificări.

Perioada de stocare a jurnalului de replicare depinde de intensitatea scrierii la acest subiect; Kafka vă permite să setați în mod flexibil limite pentru timpul de stocare și volumul de date. Pentru subiectele intensive, este important ca toți consumatorii să aibă timp să citească informațiile înainte ca acestea să dispară, chiar și în cazul inoperabilității pe termen scurt. De obicei, este posibil să stocați date pentru unități de zile, ceea ce este suficient pentru sprijin.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

În continuare, o mică repovestire a documentației, pentru cei care nu sunt familiarizați cu Kafka (poza este tot din documentație)

AMQP are cozi: scriem mesaje într-o coadă pentru consumator. De obicei, o coadă este procesată de un sistem cu aceeași logică de afaceri. Dacă trebuie să notificați mai multe sisteme, puteți învăța aplicația să scrie în mai multe cozi sau să configurați schimbul cu mecanismul fanout, care le clonează singur.

Kafka are o abstractizare similară subiect, în care scrieți mesaje, dar acestea nu dispar după citire. În mod implicit, când vă conectați la Kafka, primiți toate mesajele și aveți opțiunea de a salva de unde ați rămas. Adică citiți secvențial, este posibil să nu marcați mesajul ca citit, ci să salvați id-ul din care apoi puteți continua citirea. Id-ul pe care l-ați stabilit se numește offset, iar mecanismul este commit offset.

În consecință, poate fi implementată o logică diferită. De exemplu, avem BOB în 4 cazuri pentru diferite țări - Lamoda este în Rusia, Kazahstan, Ucraina, Belarus. Deoarece sunt implementate separat, au configurații ușor diferite și propria lor logică de afaceri. Indicăm în mesaj la ce țară se referă. Fiecare consumator BOB din fiecare țară citește cu un grup ID diferit, iar dacă mesajul nu se aplică lor, îl omite, adică. comite imediat offset +1. Dacă același subiect este citit de Serviciul nostru de plată, atunci o face cu un grup separat și, prin urmare, compensațiile nu se intersectează.

Cerințe pentru eveniment:

  • Completitudinea datelor. Aș dori ca evenimentul să aibă suficiente date pentru a putea fi procesat.

  • Integritate. Delegăm lui Events-bus verificarea că evenimentul este consecvent și îl poate procesa.
  • Ordinea este importantă. În cazul unei întoarceri, suntem nevoiți să lucrăm cu istoria. Cu notificări, comanda nu este importantă, dacă sunt notificări omogene, emailul va fi același indiferent de ce comandă a sosit prima. În cazul unei rambursări, există un proces clar; dacă schimbăm comanda, vor apărea excepții, rambursarea nu va fi creată sau procesată - vom ajunge într-o stare diferită.
  • Consecvență. Avem un magazin, iar acum creăm evenimente în loc de un API. Avem nevoie de o modalitate de a transmite rapid și ieftin informații despre evenimente noi și modificări ale celor existente la serviciile noastre. Acest lucru se realizează printr-o specificație comună într-un depozit separat git și generatoare de cod. Prin urmare, clienții și serverele din diferite servicii sunt coordonate.

Kafka în Lamoda

Avem trei instalații Kafka:

  1. Busteni;
  2. C&D;
  3. Evenimente-autobuz.

Astăzi vorbim doar despre ultimul punct. La events-bus nu avem instalații foarte mari - 3 brokeri (servere) și doar 27 de subiecte. De regulă, un subiect este un proces. Dar acesta este un punct subtil și îl vom atinge acum.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Mai sus este graficul rps. Procesul de rambursare este marcat cu o linie turcoaz (da, cea de pe axa X), iar linia roz este procesul de actualizare a conținutului.

Catalogul Lamoda conține milioane de produse, iar datele sunt actualizate tot timpul. Unele colecții ies din modă, sunt lansate altele noi pentru a le înlocui, iar în catalog apar constant modele noi. Încercăm să anticipăm ce va fi interesant pentru clienții noștri mâine, așa că achiziționăm constant lucruri noi, le fotografiem și actualizăm vitrina.

Vârfurile roz sunt actualizări ale produselor, adică modificări ale produselor. Se vede că băieții au făcut poze, au făcut poze și apoi din nou! — a încărcat un pachet de evenimente.

Cazuri de utilizare Lamoda Events

Utilizăm arhitectura construită pentru următoarele operații:

  • Urmărirea stării returnării: îndemn și urmărirea stării de la toate sistemele implicate. Plata, statusuri, fiscalizare, notificari. Aici am testat abordarea, am creat instrumente, am colectat toate erorile, am scris documentație și le-am spus colegilor noștri cum să o folosească.
  • Actualizarea cardurilor de produse: configurație, metadate, caracteristici. Un sistem citește (care afișează) și mai multe scriu.
  • E-mail, push și sms: comanda a fost ridicată, comanda a sosit, returul a fost acceptat etc., sunt o mulțime.
  • Stoc, reînnoire depozit — actualizare cantitativă a articolelor, doar numere: sosire la depozit, retur. Este necesar ca toate sistemele asociate cu rezervarea bunurilor să funcționeze cu cele mai actuale date. În prezent, sistemul de actualizare a stocurilor este destul de complex; Kafka îl va simplifica.
  • Analiza datelor (departamentul R&D), instrumente ML, analize, statistici. Vrem ca informațiile să fie transparente - Kafka este potrivit pentru asta.

Acum partea mai interesantă despre marile denivelări și descoperirile interesante care au avut loc în ultimele șase luni.

Probleme de proiectare

Să presupunem că vrem să facem ceva nou - de exemplu, să transferăm întregul proces de livrare către Kafka. Acum o parte a procesului este implementată în Procesarea comenzilor în BOB. Există un model de stare în spatele transferului unei comenzi către serviciul de livrare, deplasării către un depozit intermediar și așa mai departe. Există un întreg monolit, chiar și doi, plus o grămadă de API-uri dedicate livrării. Ei știu mult mai multe despre livrare.

Acestea par a fi zone similare, dar Procesarea comenzilor în BOB și Sistemul de expediere au stări diferite. De exemplu, unele servicii de curierat nu trimit stări intermediare, ci doar cele finale: „livrat” sau „pierdut”. Alții, dimpotrivă, relatează foarte detaliat despre circulația mărfurilor. Fiecare are propriile reguli de validare: pentru unii, e-mailul este valid, ceea ce înseamnă că va fi procesat; pentru altii nu este valabila, dar comanda va fi totusi procesata deoarece exista un numar de telefon pentru contact, iar cineva va spune ca o astfel de comanda nu va fi procesata deloc.

Flux de date

În cazul lui Kafka, se pune problema organizării fluxului de date. Această sarcină presupune alegerea unei strategii bazată pe mai multe puncte; să le parcurgem pe toate.

Într-un singur subiect sau în altele?

Avem o specificație pentru eveniment. În BOB scriem că o astfel de comandă trebuie să fie livrată și indicăm: numărul comenzii, compoziția acesteia, unele SKU-uri și coduri de bare etc. Când mărfurile ajung la depozit, livrarea va putea primi stări, marcaje temporale și tot ce este necesar. Dar apoi vrem să primim actualizări despre aceste date în BOB. Avem un proces invers de primire a datelor de la livrare. Este același eveniment? Sau este acesta un schimb separat care merită propriul subiect?

Cel mai probabil, vor fi foarte asemănătoare, iar tentația de a face un subiect nu este neîntemeiată, pentru că un subiect separat înseamnă consumatori separați, configurații separate, o generație separată a tuturor acestor lucruri. Dar nu un fapt.

Domeniu nou sau eveniment nou?

Dar dacă utilizați aceleași evenimente, atunci apare o altă problemă. De exemplu, nu toate sistemele de livrare pot genera tipul de DTO pe care BOB îl poate genera. Le trimitem id-ul, dar ei nu îl salvează pentru că nu au nevoie de el, iar din punctul de vedere al pornirii procesului event-bus, acest câmp este obligatoriu.

Dacă introducem o regulă pentru event-bus că acest câmp este obligatoriu, atunci suntem forțați să setăm reguli de validare suplimentare în BOB sau în handler-ul de start al evenimentelor. Validarea începe să se răspândească pe tot parcursul serviciului - acest lucru nu este foarte convenabil.

O altă problemă este tentația dezvoltării incrementale. Ni se spune că la eveniment trebuie adăugat ceva și poate, dacă ne gândim bine, ar fi trebuit să fie un eveniment separat. Dar în schema noastră, un eveniment separat este un subiect separat. Un subiect separat este întregul proces pe care l-am descris mai sus. Dezvoltatorul este tentat să adauge pur și simplu un alt câmp la schema JSON și să-l regenereze.

În cazul rambursărilor, am ajuns la eveniment în jumătate de an. Am avut un metaeveniment numit actualizare de rambursare, care avea un câmp de tip care descrie ce a fost de fapt această actualizare. Din această cauză, am avut comutatoare „minunate” cu validatori care ne-au spus cum să validăm acest eveniment cu acest tip.

Versiune pentru evenimente

Pentru a valida mesajele în Kafka, puteți utiliza Avro, dar a fost necesar să se întindă imediat pe el și să se folosească Confluent. În cazul nostru, trebuie să fim atenți la versiunea. Nu va fi întotdeauna posibil să recitiți mesajele din jurnalul de replicare deoarece modelul a „plecat”. Practic, se dovedește a construi versiuni astfel încât modelul să fie compatibil cu înapoi: de exemplu, faceți un câmp temporar opțional. Dacă diferențele sunt prea puternice, începem să scriem într-un subiect nou și transferăm clienții când termină de citit pe cel vechi.

Ordinea de citire garantată a partițiilor

Subiectele din interiorul Kafka sunt împărțite în partiții. Acest lucru nu este foarte important în timp ce proiectăm entități și schimburi, dar este important atunci când decidem cum să le consumăm și să le extindem.

În cazul obișnuit, scrii un subiect în Kafka. În mod implicit, este utilizată o partiție și toate mesajele din acest subiect merg la ea. Și, în consecință, consumatorul citește aceste mesaje secvenţial. Să presupunem că acum trebuie să extindem sistemul, astfel încât mesajele să fie citite de doi consumatori diferiți. Dacă, de exemplu, trimiteți SMS-uri, atunci îi puteți spune lui Kafka să facă o partiție suplimentară, iar Kafka va începe să împartă mesajele în două părți - jumătate aici, jumătate aici.

Cum îi împarte Kafka? Fiecare mesaj are un corp (în care stocăm JSON) și o cheie. Puteți atașa o funcție hash la această cheie, care va determina în ce partiție va intra mesajul.

În cazul nostru cu rambursări, acest lucru este important, dacă luăm două partiții, atunci există șansa ca un consumator paralel să proceseze al doilea eveniment înainte de primul și să apară probleme. Funcția hash asigură că mesajele cu aceeași cheie ajung în aceeași partiție.

Evenimente vs comenzi

Aceasta este o altă problemă pe care am întâlnit-o. Evenimentul este un anumit eveniment: spunem că ceva sa întâmplat undeva (something_happened), de exemplu, un articol a fost anulat sau a avut loc o rambursare. Dacă cineva ascultă aceste evenimente, atunci în funcție de „articol anulat”, va fi creată entitatea de rambursare, iar „rambursarea a avut loc” va fi scris undeva în setări.

Dar, de obicei, atunci când proiectați evenimente, nu doriți să le scrieți în zadar - vă bazați pe faptul că cineva le va citi. Există o tentație mare de a scrie nu ceva_sa întâmplat (articol_anulat, rambursare_rambursat), ci ceva_trebuie_făcut. De exemplu, articolul este gata pentru a fi returnat.

Pe de o parte, sugerează cum va fi utilizat evenimentul. Pe de altă parte, sună mult mai puțin ca un nume de eveniment normal. În plus, nu este departe de aici comanda do_something. Dar nu aveți nicio garanție că cineva a citit acest eveniment; iar dacă îl citești, atunci îl citești cu succes; iar dacă ai citit-o cu succes, atunci ai făcut ceva și acel ceva a avut succes. În momentul în care un eveniment devine fă_ceva, feedback-ul devine necesar și asta este o problemă.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

În schimbul asincron în RabbitMQ, când citiți mesajul, mergeți la http, aveți un răspuns - cel puțin că mesajul a fost primit. Când îi scrii lui Kafka, există un mesaj pe care l-ai scris lui Kafka, dar nu știi nimic despre cum a fost procesat.

Prin urmare, în cazul nostru, a trebuit să introducem un eveniment de răspuns și să setăm monitorizarea astfel încât, dacă s-au trimis atâtea evenimente, după așa și într-un timp să sosească același număr de evenimente de răspuns. Dacă acest lucru nu se întâmplă, atunci ceva pare să fi mers prost. De exemplu, dacă am trimis evenimentul „item_ready_to_refund”, ne așteptăm ca o rambursare să fie creată, banii să fie returnați clientului, iar evenimentul „money_refunded” să ne fie trimis. Dar acest lucru nu este sigur, așa că este necesară monitorizarea.

nuanțe

Există o problemă destul de evidentă: dacă citești dintr-un subiect secvenţial și ai un mesaj rău, consumatorul va cădea, iar tu nu vei merge mai departe. Ai nevoie oprește toți consumatorii, comite offset în continuare pentru a continua lectura.

Știam despre asta, ne bazam pe asta și totuși s-a întâmplat. Și asta s-a întâmplat pentru că evenimentul a fost valabil din punctul de vedere al events-bus, evenimentul a fost valabil din punctul de vedere al validatorului aplicației, dar nu a fost valabil din punctul de vedere al PostgreSQL, deoarece în sistemul nostru unic MySQL cu UNSIGNED INT, iar în noul scris sistemul avea PostgreSQL doar cu INT. Dimensiunea lui este puțin mai mică, iar Id-ul nu se potrivește. Symfony a murit cu o excepție. Desigur, am prins excepția pentru că ne-am bazat pe ea și urma să comitem acest offset, dar înainte de asta am vrut să creștem contorul de probleme, deoarece mesajul a fost procesat fără succes. Contoarele din acest proiect sunt, de asemenea, în baza de date, iar Symfony a închis deja comunicarea cu baza de date, iar a doua excepție a ucis întregul proces fără șansa de a comite offset.

Serviciul s-a întins de ceva timp - din fericire, cu Kafka acest lucru nu este atât de rău, pentru că mesajele rămân. Când munca este restabilită, le puteți termina de citit. Este confortabil.

Kafka are capacitatea de a seta un offset arbitrar prin scule. Dar pentru a face acest lucru, trebuie să opriți toți consumatorii - în cazul nostru, pregătiți o versiune separată în care nu vor exista consumatori, redistribuiri. Apoi, în Kafka, puteți schimba offset-ul prin scule, iar mesajul va trece.

O altă nuanță - jurnal de replicare vs rdkafka.so - este legat de specificul proiectului nostru. Folosim PHP, iar în PHP, de regulă, toate bibliotecile comunică cu Kafka prin depozitul rdkafka.so, iar apoi există un fel de wrapper. Poate că acestea sunt dificultățile noastre personale, dar s-a dovedit că simpla recitire a unui fragment din ceea ce citisem deja nu este atât de ușoară. În general, au existat probleme de software.

Revenind la specificul lucrului cu partiții, este scris chiar în documentație consumers >= topic partitions. Dar am aflat despre asta mult mai târziu decât mi-aș fi dorit. Dacă doriți să scalați și să aveți doi consumatori, aveți nevoie de cel puțin două partiții. Adică, dacă ai avut o partiție în care s-au acumulat 20 de mii de mesaje și ai făcut una nouă, numărul de mesaje nu va fi egalat în curând. Prin urmare, pentru a avea doi consumatori paraleli, trebuie să vă ocupați de partiții.

monitorizarea

Cred că modul în care vom monitoriza va fi și mai clar ce probleme există în abordarea existentă.

De exemplu, calculăm câte produse din baza de date și-au schimbat recent starea și, în consecință, evenimentele ar fi trebuit să aibă loc pe baza acestor modificări și trimitem acest număr sistemului nostru de monitorizare. Apoi de la Kafka obținem al doilea număr, câte evenimente au fost de fapt înregistrate. Evident, diferența dintre aceste două numere ar trebui să fie întotdeauna zero.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

În plus, trebuie să monitorizați cum se descurcă producătorul, dacă autobuzul de evenimente a primit mesaje și cum se descurcă consumatorul. De exemplu, în graficele de mai jos, Instrumentul de rambursare merge bine, dar BOB are în mod clar unele probleme (vârfurile albastre).

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Am menționat deja decalajul grupului de consumatori. În linii mari, acesta este numărul de mesaje necitite. În general, consumatorii noștri lucrează rapid, deci decalajul este de obicei 0, dar uneori poate exista un vârf pe termen scurt. Kafka poate face acest lucru din cutie, dar trebuie să setați un anumit interval.

Există un proiect Vizuinăcare vă va oferi mai multe informații despre Kafka. Pur și simplu folosește API-ul grupului de consumatori pentru a oferi starea cum se descurcă acest grup. Pe lângă OK și eșuat, există un avertisment și poți afla că consumatorii tăi nu pot face față ritmului de producție - nu au timp să corecteze ceea ce este scris. Sistemul este destul de inteligent și ușor de utilizat.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

Așa arată răspunsul API. Aici este grupul bob-live-fifa, partiția refund.update.v1, status OK, lag 0 - ultimul offset final așa și așa.

Experiență în dezvoltarea serviciului Refund Tool cu ​​un API asincron pe Kafka

monitorizarea updated_at SLA (blocat) am mentionat deja. De exemplu, produsul s-a schimbat în starea că este gata pentru returnare. Instalăm Cron, care spune că dacă în 5 minute acest obiect nu a mers la rambursare (returnăm banii prin sistemele de plată foarte repede), atunci ceva cu siguranță a mers prost, iar acesta este cu siguranță un caz de suport. Prin urmare, pur și simplu luăm Cron, care citește astfel de lucruri, iar dacă sunt mai mari decât 0, atunci trimite o alertă.

Pentru a rezuma, utilizarea evenimentelor este convenabilă când:

  • informațiile sunt necesare mai multor sisteme;
  • rezultatul prelucrării nu este important;
  • sunt puține evenimente sau evenimente mici.

S-ar părea că articolul are un subiect foarte specific - API asincron pe Kafka, dar în legătură cu acesta aș dori să recomand o mulțime de lucruri deodată.
În primul rând, în continuare HighLoad ++ trebuie să așteptăm până în noiembrie, în aprilie va exista o versiune Sankt Petersburg, iar în iunie vom vorbi despre încărcături mari în Novosibirsk.
În al doilea rând, autorul raportului, Serghei Zaika, este membru al Comitetului de Program al noii noastre conferințe despre managementul cunoștințelor. KnowledgeConf. Conferința este de o zi, va avea loc pe 26 aprilie, dar programul ei este foarte intens.
Și va fi în mai PHP Rusia и RIT++ (cu DevOpsConf inclus) - poți, de asemenea, să sugerezi subiectul tău acolo, să vorbești despre experiența ta și să te plângi de conurile tale umplute.

Sursa: www.habr.com

Adauga un comentariu