{"id":30778,"date":"2019-10-31T21:37:23","date_gmt":"2019-10-31T18:37:23","guid":{"rendered":"https:\/\/prohoster.info\/blog\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\/"},"modified":"2019-10-31T21:37:23","modified_gmt":"2019-10-31T18:37:23","slug":"opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","status":"publish","type":"post","link":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ce ar putea determina o companie at\u00e2t de mare precum Lamoda, cu un proces bine pus la punct \u0219i zeci de servicii interconectate, s\u0103 \u00ee\u0219i schimbe semnificativ abordarea? Motiva\u021biile pot fi foarte diverse: de la cele legale la dorin\u021ba inerent\u0103 a tuturor programatorilor de a experimenta.<\/p>\n<p>Dar asta nu \u00eenseamn\u0103 c\u0103 nu se poate conta pe beneficii suplimentare. Ce anume se poate c\u00e2\u0219tiga dac\u0103 se implementeaz\u0103 un API bazat pe evenimente pe Kafka, va explica Serghei Zaika (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). Vor fi, de asemenea, discu\u021bii despre gre\u0219elile \u00eenv\u0103\u021bate \u0219i descoperirile interesante \u2014 nu poate exista un experiment f\u0103r\u0103 acestea.<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Disclaimer: Acest articol se bazeaz\u0103 pe materialele meetup-ului pe care Serghei l-a organizat \u00een noiembrie 2018 la HighLoad++. Experien\u021ba real\u0103 a Lamoda \u00een utilizarea Kafka a atras cu siguran\u021b\u0103 aten\u021bia la fel de mult ca \u0219i celelalte prezent\u0103ri din program. Ni se pare c\u0103 acesta este un exemplu excelent al faptului c\u0103 este \u00eentotdeauna posibil \u0219i necesar s\u0103 g\u0103se\u0219ti oameni cu acelea\u0219i idei, iar organizatorii HighLoad++ vor continua s\u0103 \u00eencerce s\u0103 creeze o atmosfer\u0103 care s\u0103 \u00eencurajeze acest lucru.<\/em><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Despre proces<\/h2>\n<p>\nLamoda este o platform\u0103 e-commerce mare, care are propriul s\u0103u centru de contact, serviciu de livrare (\u0219i numeroase parteneriate), studio foto, un depozit imens \u0219i toate acestea func\u021bioneaz\u0103 pe propriul software. Exist\u0103 zeci de metode de plat\u0103, parteneri B2B care pot folosi unele sau toate aceste servicii \u0219i doresc s\u0103 cunoasc\u0103 informa\u021biile actualizate despre produsele lor. \u00cen plus, Lamoda activeaz\u0103 \u00een trei \u021b\u0103ri \u00een afar\u0103 de RF \u0219i acolo lucrurile sunt pu\u021bin diferite. \u00cen total, exist\u0103 probabil mai mult de o sut\u0103 de moduri de a configura o nou\u0103 comand\u0103, care trebuie procesat\u0103 \u00een mod specific. Toate acestea func\u021bioneaz\u0103 cu ajutorul a zeci de servicii care comunic\u0103 uneori \u00eentr-un mod mai pu\u021bin evident. Exist\u0103, de asemenea, un sistem central, a c\u0103rui responsabilitate principal\u0103 este gestionarea statutelor comenzilor. O numim BOB, iar eu lucrez cu acesta.<\/p>\n<h2>Refund Tool with events-driven API <\/h2>\n<p>\nCuv\u00e2ntul events-driven este destul de folosit, iar mai t\u00e2rziu vom defini \u00een detaliu ce \u00eenseamn\u0103 acest lucru. Voi \u00eencepe cu contextul \u00een care am decis s\u0103 test\u0103m abordarea API-ului bazat pe evenimente \u00een Kafka. <\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u00cen orice magazin, pe l\u00e2ng\u0103 comenzile pentru care clien\u021bii pl\u0103tesc, exist\u0103 momente c\u00e2nd magazinul este nevoit s\u0103 returneze bani, deoarece clientului nu i s-a potrivit produsul. Acest proces relativ scurt: confirm\u0103m informa\u021biile, dac\u0103 este necesar, \u0219i transfer\u0103m banii. <\/p>\n<p>\u00cens\u0103, returnarea s-a complicat din cauza modific\u0103rilor legislative, iar noi am fost nevoi\u021bi s\u0103 implement\u0103m un microserviciu separat pentru aceasta.<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e358df5476e7448976e0f1147a103fb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMotiva\u021bia noastr\u0103:<\/p>\n<ol>\n<li><strong>Legea FZ-54<\/strong>\u00a0\u2014 pe scurt, legea cere s\u0103 raport\u0103m autorit\u0103\u021bilor fiscale fiecare opera\u021biune financiar\u0103, fie c\u0103 este vorba de returnare sau de venit, \u00eentr-un termen destul de scurt, de c\u00e2teva minute. Ca e-commerce, realiz\u0103m un num\u0103r considerabil de opera\u021biuni. Tehnic, aceasta \u00eenseamn\u0103 o nou\u0103 responsabilitate (\u0219i, prin urmare, un nou serviciu) \u0219i modific\u0103ri \u00een toate sistemele implicate.<\/li>\n<li><strong>BOB split<\/strong>\u00a0\u2014 un proiect intern al companiei pentru a elibera BOB de un num\u0103r mare de responsabilit\u0103\u021bi externe \u0219i a reduce complexitatea sa general\u0103.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u00cen acest diagram\u0103 sunt ilustrate sistemele principale ale Lamoda. \u00cen prezent, majoritatea dintre ele reprezint\u0103 mai mult <strong>o constela\u021bie de 5-10 microservicii \u00een jurul unui monolit \u00een diminuare.<\/strong>Ele cresc u\u0219or, dar ne str\u0103duim s\u0103 le facem mai pu\u021bine, deoarece a desf\u0103\u0219ura un fragment dedicat \u00een mijloc este \u00eenfrico\u0219\u0103tor \u2014 trebuie s\u0103 ne asigur\u0103m c\u0103 nu va ceda. Toate interac\u021biunile (sagetele) trebuie rezervate, presupun\u00e2nd c\u0103 oricare dintre ele poate deveni inaccesibil.<\/p>\n<p>\u00cen BOB exist\u0103 de asemenea multe interac\u021biuni: sisteme de plat\u0103, livrare, notific\u0103ri etc. <\/p>\n<p>Tehnic, BOB este:<\/p>\n<ul>\n<li>~150k linii de cod + ~100k linii de teste;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Components 3;<\/li>\n<li>&gt;100 API &amp; ~50 integra\u021bii externe;<\/li>\n<li>4 \u021b\u0103ri cu logica lor de afaceri. <\/li>\n<\/ul>\n<p>\nA desf\u0103\u0219ura BOB este costisitor \u0219i dureros, volumul de cod \u0219i sarcinile pe care le rezolv\u0103 sunt astfel \u00eenc\u00e2t nimeni nu poate \u00eenv\u0103\u021ba totul despre el \u00een \u00eentregime. \u00cen general, exist\u0103 multe motive pentru a-l simplifica.<\/p>\n<h2>Procesul de returnare<\/h2>\n<p>\nIni\u021bial, procesul implic\u0103 dou\u0103 sisteme: BOB \u0219i Payment. Acum apar \u00eenc\u0103 dou\u0103:<\/p>\n<ul>\n<li>Fiscalization Service, care se va ocupa de problemele de fiscalizare \u0219i de comunicarea cu serviciile externe.<\/li>\n<li>Refund Tool, \u00een care se transfer\u0103 pur \u0219i simplu noile interac\u021biuni pentru a nu umfla BOB.<\/li>\n<\/ul>\n<p>\nAcum procesul arat\u0103 astfel:<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/13c02975881ad35c61304053c604cda3.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ol>\n<li>BOB prime\u0219te o cerere de returnare a banilor.<\/li>\n<li>BOB notific\u0103 Refund Tool despre aceasta.<\/li>\n<li>Refund Tool spune Payment: \u201eReturna\u021bi banii\u201d.<\/li>\n<li>Payment returneaz\u0103 banii.<\/li>\n<li>Refund Tool \u0219i BOB sincronizeaz\u0103 \u00eentre ei statusurile, deoarece deocamdat\u0103 am\u00e2ndou\u0103 au nevoie de acest lucru. Nu suntem \u00eenc\u0103 preg\u0103ti\u021bi s\u0103 ne \u00eentoarcem pe deplin la Refund Tool, deoarece \u00een BOB exist\u0103 un UI, rapoarte pentru contabilitate \u0219i, \u00een general, multe date care nu pot fi transferate at\u00e2t de u\u0219or. Trebuie s\u0103 ne men\u021binem pe dou\u0103 scaune.<\/li>\n<li>Se trimite o cerere pentru fiscalizare.<\/li>\n<\/ol>\n<p>\n\u00cen concluzie, am creat pe Kafka un fel de bus de evenimente - event-bus, pe care s-au legat toate. Ura, acum avem un singur punct de e\u0219ec (sarcasm).<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPro \u0219i contra sunt destul de evidente. Am creat un bus, ceea ce \u00eenseamn\u0103 c\u0103 acum toate serviciile depind de el. Aceasta simplific\u0103 proiectarea, dar introduce \u00een sistem un punct unic de e\u0219ec. Dac\u0103 Kafka se pr\u0103bu\u0219e\u0219te, procesul se opre\u0219te.<\/p>\n<h2>Ce este un API bazat pe evenimente <\/h2>\n<p>\nUn r\u0103spuns bun la aceast\u0103 \u00eentrebare se g\u0103se\u0219te \u00een prezentarea lui Martin Fowler (GOTO 2017) <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u201eMulte \u00een\u021belesuri ale arhitecturii bazate pe evenimente\u201d<\/a><\/noindex>. <\/p>\n<p>Pe scurt, ce am f\u0103cut:<\/p>\n<ol>\n<li>Am \u00eembr\u0103cat toate schimburile asincrone prin <strong>stocarea evenimentelor<\/strong>. \u00cen loc s\u0103 comunic\u0103m prin re\u021bea fiec\u0103rui consumator interesat despre schimbarea st\u0103rii, scriem \u00eentr-un depozit centralizat un eveniment despre schimbarea de stare, iar consumatorii interesa\u021bi de subiect citesc de acolo tot ce apare.<\/li>\n<li>Un eveniment (event) \u00een acest caz este o notificare (<strong>notifications<\/strong>) despre faptul c\u0103 ceva s-a schimbat undeva. De exemplu, s-a schimbat statul unei comenzi. Consumatorul, c\u0103ruia \u00eei sunt importante anumite date de acompaniament la schimbarea st\u0103rii \u0219i care nu sunt \u00een notificare, poate afla starea lor singur.<\/li>\n<li>Varianta maxim\u0103 - un sourcing de evenimente complet, <strong>transfer de stare<\/strong>, \u00een care evenimentul con\u021bine toat\u0103 informa\u021bia necesar\u0103 pentru procesare: de unde \u0219i \u00een ce stare am trecut, cum s-au schimbat datele etc. \u00centrebarea este doar despre fezabilitate \u0219i volumul de informa\u021bie pe care \u00ee\u021bi po\u021bi permite s\u0103-l stochezi.<\/li>\n<\/ol>\n<p>\n\u00cen cadrul lans\u0103rii instrumentului Refund, am folosit a treia variant\u0103. Acest lucru a simplificat procesarea evenimentelor, deoarece nu a fost necesar s\u0103 ob\u021binem informa\u021bii detaliate, plus a exclus scenariul \u00een care fiecare nou eveniment genereaz\u0103 un v\u00e2rf de solicit\u0103ri de tip get pentru clarific\u0103ri din partea consumatorilor.<\/p>\n<p>Serviciul Refund Tool <strong>nu este suprasolicitat<\/strong>, astfel c\u0103 Kafka acolo este mai mult o prob\u0103 dec\u00e2t o necesitate. Nu cred c\u0103, dac\u0103 serviciul de returnare a fondurilor ar deveni un proiect de tip high-load, afacerea ar fi mul\u021bumit\u0103.<\/p>\n<h4>Schimb asincron A\u0218A CUM ESTE<\/h4>\n<p>\nPentru schimburile asincrone, departamentul de PHP folose\u0219te de obicei RabbitMQ. Am adunat datele pentru solicitare, le-am pus \u00een coad\u0103 \u0219i consumatorul aceluia\u0219i serviciu le-a citit \u0219i le-a trimis (sau nu le-a trimis). Pentru API-ul propriu, Lamoda folose\u0219te activ Swagger. Proiect\u0103m API-ul, \u00eel descriem \u00een Swagger, gener\u0103m codul client \u0219i server. De asemenea, folosim un JSON RPC 2.0 pu\u021bin extins. <\/p>\n<p>\u00cen unele locuri se folosesc busuri esb, cineva tr\u0103ie\u0219te pe activeMQ, dar, \u00een general, <strong>RabbitMQ - standard<\/strong>.<\/p>\n<h4>Async exchange TO BE<\/h4>\n<p>\nProiect\u00e2nd un schimb prin events-bus, se observ\u0103 o analogie. Asem\u0103n\u0103tor, descriem viitorul schimb de date prin descrierea structurii event-ului. Formatul yaml, generarea de coduri a trebuit s\u0103 o facem noi \u00een\u0219ine, generatorul conform specifica\u021biei creeaz\u0103 DTO-uri \u0219i \u00eenva\u021b\u0103 clien\u021bii \u0219i serverele s\u0103 lucreze cu acestea. Generarea se face \u00een dou\u0103 limbaje - <strong>golang \u0219i php<\/strong>. Acest lucru permite men\u021binerea bibliotecilor consecvente. Generatorul este scris \u00een golang, motiv pentru care a primit numele gogi.<\/p>\n<p>Event-sourcing pe Kafka este o practic\u0103 obi\u0219nuit\u0103. Exist\u0103 o solu\u021bie de la versiunea enterprise principal\u0103 Kafka Confluent, exist\u0103 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">nakadi<\/a><\/noindex>, o solu\u021bie de la \u00abfra\u021bii\u00bb no\u0219tri din domeniul Zalando. Motiva\u021bia noastr\u0103 de a \u00eencepe cu vanilla Kafka <strong>este s\u0103 men\u021binem solu\u021bia gratuit\u0103, p\u00e2n\u0103 c\u00e2nd ne decidem dac\u0103 vom folosi pe scar\u0103 larg\u0103, precum \u0219i s\u0103 ne l\u0103s\u0103m un spa\u021biu de manevr\u0103 \u0219i \u00eembun\u0103t\u0103\u021biri: vrem suport pentru propriul<\/strong>\u00a0JSON RPC 2.0 <strong>, generatoare pentru dou\u0103 limbaje \u0219i s\u0103 vedem ce altceva.<\/strong>Ironia este c\u0103, chiar \u0219i \u00een cazul fericit \u00een care exist\u0103 un business similar Zalando, care a realizat o solu\u021bie asem\u0103n\u0103toare, nu putem utiliza eficient. <\/p>\n<p>Arhitectural, \u00een lansare, patternul este urm\u0103torul: citim direct din Kafka, dar scriem doar prin events-bus. Pentru citirea din Kafka exist\u0103 multe solu\u021bii gata: brokeri, echilibratori \u0219i este mai mult sau mai pu\u021bin preg\u0103tit\u0103 pentru scalare orizontal\u0103, acest lucru ne-am dorit s\u0103 p\u0103str\u0103m. Scrierea, \u00eens\u0103, am dorit s\u0103 o \u00eempachet\u0103m printr-un Gateway aka Events-bus, \u0219i iat\u0103 motivul. <\/p>\n<p>Events-bus<\/p>\n<h3>sau autobuz de evenimente. Este pur \u0219i simplu un gateway HTTP stateless, care \u00ee\u0219i asum\u0103 c\u00e2teva roluri importante:<\/h3>\n<p>\nValidarea producerii<\/p>\n<ul>\n<li><strong>\u2014 verific\u0103m c\u0103 evenimentele respect\u0103 specifica\u021bia noastr\u0103.<\/strong>\u00a0Sistemul principal pentru evenimente<\/li>\n<li><strong>, adic\u0103 este singurul sistem din companie care r\u0103spunde la \u00eentrebarea, care evenimente cu ce structuri sunt considerate valide. \u00cen validare intr\u0103 doar tipurile de date \u0219i enums pentru specifica\u021bia strict\u0103 a con\u021binutului.<\/strong>Func\u021bia de hash <\/li>\n<li><strong>pentru sharding - structura mesajului Kafka este key-value \u0219i aici, pe baza hash-ului de la key se calculeaz\u0103 unde trebuie s\u0103 fie plasat.<\/strong> De ce<\/li>\n<\/ul>\n<p><\/p>\n<h3>Lucr\u0103m \u00eentr-o mare companie cu un proces bine stabilit. De ce am schimba ceva?<\/h3>\n<p>\nEste un experiment <strong>, \u0219i ne a\u0219tept\u0103m s\u0103 ob\u021binem c\u00e2teva beneficii.<\/strong>1:n+1 schimburi (unu la mul\u021bi)<\/p>\n<h4>Este foarte simplu s\u0103 conectezi API-uri pentru noi consumatori cu Kafka.<\/h4>\n<p>\nCu Kafka, este foarte simplu s\u0103 conecta\u021bi noi consumatori la API. <\/p>\n<p>S\u0103 presupunem c\u0103 ave\u021bi un director pe care trebuie s\u0103 \u00eel men\u021bine\u021bi actualizat \u00een mai multe sisteme simultan (\u0219i \u00een unele noi). \u00cen trecut, inventasem un bundle care implementa set-API, iar \u00een sistemul principal comunicam adresele consumatorilor. Acum, sistemul principal trimite actualiz\u0103ri c\u0103tre un topic, \u0219i to\u021bi cei interesa\u021bi le citesc. A ap\u0103rut un nou sistem \u2014 l-am abonat la topic. Da, tot bundle, dar mai simplu.<\/p>\n<p>\u00cen cazul tool-ului de rambursare, care este de fapt o parte a BOB, ne este convenabil s\u0103 le men\u021binem sincronizate prin Kafka. Payment spune c\u0103 banii au fost returna\u021bi: BOB \u0219i RT au aflat despre asta, \u0219i-au actualizat statusurile, iar Fiscalization Service a aflat \u0219i a emis chitan\u021ba.<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPl\u0103nuim s\u0103 facem un Serviciu de Notific\u0103ri unic, care s\u0103 informeze clientul despre nout\u0103\u021bile privind comanda\/return\u0103rile sale. Acum, aceast\u0103 responsabilitate este dispersat\u0103 \u00eentre sisteme. Ne va fi suficient s\u0103 \u00eenv\u0103\u021b\u0103m Serviciul de Notific\u0103ri s\u0103 extrag\u0103 informa\u021bii relevante din Kafka \u0219i s\u0103 reac\u021bioneze la ele (\u0219i s\u0103 dezactiv\u0103m aceste notific\u0103ri \u00een celelalte sisteme). Nu va fi necesar s\u0103 facem schimburi directe noi.<\/p>\n<h4>Data-driven<\/h4>\n<p>\nInforma\u021bia \u00eentre sisteme devine transparent\u0103 \u2014 indiferent de c\u00e2t de complex ar fi \u00abenterprise\u00bb-ul vostru \u0219i de c\u00e2t de mare ar fi backlog-ul vostru. \u00cen Lamoda exist\u0103 un departament de Data Analytics care colecteaz\u0103 date din sisteme \u0219i le transform\u0103 \u00eentr-o form\u0103 reutilizabil\u0103, at\u00e2t pentru afaceri, c\u00e2t \u0219i pentru sistemele inteligente. Kafka permite furnizarea rapid\u0103 de multe date \u0219i men\u021binerea acestui flux informa\u021bional actualizat.<\/p>\n<h4>Replication log<\/h4>\n<p>\nMesajele nu dispar dup\u0103 citire, ca \u00een RabbitMQ. C\u00e2nd un eveniment con\u021bine suficiente informa\u021bii pentru procesare, avem o istorie a celor mai recente modific\u0103ri ale obiectului \u0219i, la nevoie, posibilitatea de a aplica aceste modific\u0103ri.<\/p>\n<p>Perioada de stocare a replication log-ului depinde de intensitatea scrierii \u00een acest topic, iar Kafka permite configurarea flexibil\u0103 a limitelor de stocare at\u00e2t \u00een timp, c\u00e2t \u0219i \u00een volum de date. Pentru topicurile intensive, este important ca to\u021bi consumatorii s\u0103 reu\u0219easc\u0103 s\u0103 citeasc\u0103 informa\u021bia \u00eenainte ca aceasta s\u0103 dispar\u0103, chiar \u0219i \u00een cazul unei nefunc\u021bion\u0103ri temporare. De obicei, reu\u0219im s\u0103 stoc\u0103m date pentru\u00a0<strong>c\u00e2teva zile<\/strong>, ceea ce este suficient pentru suport. <\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMai departe, un scurt rezumat al documenta\u021biei, pentru cei care nu sunt familiariza\u021bi cu Kafka (imaginea este de asemenea din documenta\u021bie)<\/p>\n<p>\u00cen AMQP exist\u0103 cozi: scriem mesaje \u00een coad\u0103 pentru consumator. De obicei, o coad\u0103 este prelucrat\u0103 de un singur sistem cu aceea\u0219i logic\u0103 de afaceri. Dac\u0103 este necesar s\u0103 notify mai multe sisteme, aplica\u021bia poate fi \u00eenv\u0103\u021bat\u0103 s\u0103 scrie \u00een mai multe cozi sau s\u0103 configureze un exchange cu un mecanism fanout, care le cloneaz\u0103 automat.<\/p>\n<p>\u00cen Kafka exist\u0103 o abstrac\u021bie similar\u0103 <em>topic<\/em>, \u00een care scrie\u021bi mesaje, dar acestea nu dispar dup\u0103 ce sunt citite. Implicit, c\u00e2nd v\u0103 conecta\u021bi la Kafka, ob\u021bine\u021bi toate mesajele, \u0219i exist\u0103 posibilitatea de a salva locul unde a\u021bi r\u0103mas. Asta \u00eenseamn\u0103 c\u0103 citi\u021bi secven\u021bial, pute\u021bi s\u0103 nu marca\u021bi mesajul ca fiind citit, dar s\u0103 salva\u021bi id-ul de la care ve\u021bi continua citirea. Id-ul, de la care a\u021bi r\u0103mas, se nume\u0219te offset, iar mecanismul \u2013 commit offset. <\/p>\n<p>Prin urmare, se poate implementa o logic\u0103 diferit\u0103. De exemplu, BOB exist\u0103 \u00een 4 instan\u021be pentru diferite \u021b\u0103ri \u2013 Lamoda este disponibil\u0103 \u00een Rusia, Kazahstan, Ucraina, Belarus. Deoarece sunt desf\u0103\u0219urate separat, au ceva confguri proprii \u0219i o logic\u0103 de afaceri specific\u0103. Indicam \u00een mesaj la ce \u021bar\u0103 se refer\u0103. Fiecare consumator BOB din fiecare \u021bar\u0103 cite\u0219te cu groupId-uri diferite, iar dac\u0103 un mesaj nu se refer\u0103 la el, \u00eel sar, adic\u0103 comite imediat offset +1. Dac\u0103 aceea\u0219i tem\u0103 este citit\u0103 de serviciul nostru de pl\u0103\u021bi, o face cu un grup separat, \u0219i de aceea offset-urile nu se intersecteaz\u0103.<\/p>\n<p><b>Cerin\u021be pentru evenimente:<\/b><\/p>\n<ul>\n<li><strong>Completeness of data. <\/strong>Ne-ar pl\u0103cea ca evenimentul s\u0103 aib\u0103 suficiente date pentru a putea fi procesat. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Integrity. <\/strong>Deleg\u0103m Events-bus-ului verificarea faptului c\u0103 evenimentul este consistent \u0219i c\u0103 poate fi procesat.<\/li>\n<li><strong>Ordinea este important\u0103. <\/strong>\u00cen cazul return\u0103rilor, suntem nevoi\u021bi s\u0103 lucr\u0103m cu istoria. \u00cen cazul notific\u0103rilor, ordinea nu este important\u0103, dac\u0103 sunt notific\u0103ri omogene, email-ul va fi acela\u0219i indiferent de care comand\u0103 a sosit prima. \u00cen cazul return\u0103rilor, exist\u0103 un proces clar, dac\u0103 schimb\u0103m ordinea, pot ap\u0103rea excep\u021bii, refund-ul nu se va crea sau nu va fi procesat \u2013 vom ajunge \u00eentr-un alt statut.<\/li>\n<li><strong>Coeren\u021ba. <\/strong>Avem un depozit \u0219i acum cre\u0103m evenimente \u00een loc de API-uri. Avem nevoie de un mod rapid \u0219i ieftin de a trimite informa\u021bii despre noi evenimente \u0219i despre modific\u0103rile celor existente \u00een serviciile noastre. Acest lucru se realizeaz\u0103 printr-o specifica\u021bie comun\u0103 \u00eentr-un repository git separat \u0219i generatoare de cod. Astfel, clien\u021bii \u0219i serverele din diferite servicii sunt armonizate.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka \u00een Lamoda<\/h2>\n<p>\nAvem trei instal\u0103ri Kafka: <\/p>\n<ol>\n<li>Logs;<\/li>\n<li>R&amp;D;<\/li>\n<li>Events-bus.<\/li>\n<\/ol>\n<p>\nAst\u0103zi discut\u0103m doar despre ultimul punct. \u00cen events-bus avem instal\u0103ri destul de mici \u2013 3 brokeri (servere) \u0219i \u00een total 27 de teme. De obicei, o tem\u0103 reprezint\u0103 un singur proces. Dar acesta este un aspect delicat, \u0219i \u00een cur\u00e2nd ne vom ocupa de el.<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMai sus este graficul rps. Procesul de returnare este marcat de linia turcoaz (da, da, aceea de pe axa X), iar procesul de actualizare a con\u021binutului de cea roz. <\/p>\n<p>Catalogul Lamoda con\u021bine milioane de produse, iar datele sunt actualizate constant. Unele colec\u021bii ies din mod\u0103, \u00eenlocuiesc noi produse care apar tot timpul \u00een catalog. \u00cencerc\u0103m s\u0103 prezicem ce va fi interesant pentru clien\u021bii no\u0219tri m\u00e2ine, a\u0219a c\u0103 achizi\u021bion\u0103m constant lucruri noi, le fotografiem \u0219i actualiz\u0103m vitrina. <\/p>\n<p>Pecurile roz reprezint\u0103 actualiz\u0103ri de produs, adic\u0103 modific\u0103ri ale produselor. Se vede c\u0103 echipa a fotografiat mereu, mereu, iar apoi, dintr-o dat\u0103! \u2013 au \u00eenc\u0103rcat un lot de evenimente.<\/p>\n<h2>Cazuri de utilizare Lamoda Events<\/h2>\n<p>\nArhitectura construit\u0103 este folosit\u0103 pentru astfel de opera\u021biuni:<\/p>\n<ul>\n<li><strong>Urm\u0103rirea statutelor de returnare<\/strong>: call-to-action \u0219i urm\u0103rirea statutelor din toate sistemele implicate. Plata, statuile, fiscalizarea, notific\u0103rile. Aici am testat abordarea, am creat instrumente, am adunat toate bug-urile, am scris documenta\u021bia \u0219i am explicat colegilor cum s\u0103 foloseasc\u0103 aceste instrumente.<\/li>\n<li><strong>Actualizarea fi\u0219elor de produs: <\/strong>configurare, metadate, specifica\u021bii. Cite\u0219te un singur sistem (care le afi\u0219eaz\u0103), dar mai multe le scriu.<\/li>\n<li><strong>Email, push \u0219i sms<\/strong>: comanda a fost realizat\u0103, comanda a ajuns, returnarea a fost acceptat\u0103 etc., sunt multe. <\/li>\n<li><strong>Stoc, actualizarea stocului<\/strong>\u00a0\u2013 actualizare cantitativ\u0103 a denumirilor, doar cifre: sosirea pe stoc, returnare. Este necesar ca toate sistemele legate de rezervarea produselor s\u0103 opereze cu date c\u00e2t mai actualizate. \u00cen prezent, sistemul de actualizare a stocului este destul de complex, Kafka va permite simplificarea acestuia.<\/li>\n<li><strong>Analiza datelor<\/strong> (Departamentul R&amp;D), instrumente ML, analitic\u0103, statistic\u0103. Vrem ca informa\u021bia s\u0103 fie transparent\u0103 - pentru asta Kafka este potrivit.<\/li>\n<\/ul>\n<p>\nAcum partea mai interesant\u0103 despre gre\u0219elile f\u0103cute \u0219i descoperirile interesante care au avut loc \u00een ultimele \u0219ase luni.<\/p>\n<h2>Probleme de proiectare<\/h2>\n<p>\nS\u0103 presupunem c\u0103 dorim s\u0103 realiz\u0103m o nou\u0103 nebunie - de exemplu, s\u0103 transfer\u0103m \u00eentregul proces de livrare pe Kafka. \u00cen prezent, o parte din proces este implementat\u0103 \u00een Order Processing \u00een BOB. Exist\u0103 un model de stare pentru transferul comenzii la serviciul de livrare, mutarea la depozitul intermediar \u0219i altele. Exist\u0103 un monolit \u00eentreg, chiar dou\u0103, plus o mul\u021bime de API-uri dedicate livr\u0103rii. Ele \u0219tiu mult mai multe despre livrare. <\/p>\n<p>Se pare c\u0103 acestea sunt domenii similare, dar pentru Order Processing \u00een BOB \u0219i pentru sistemul de livrare, st\u0103rile difer\u0103. De exemplu, unele servicii de curierat nu transmit st\u0103ri intermediare, ci doar finale: \u201elivrat\u201d sau \u201epierdut\u201d. Altele, dimpotriv\u0103, ofer\u0103 informa\u021bii detaliate despre mi\u0219carea bunului. Fiecare are propriile reguli de validare: pentru unii, un email valid \u00eenseamn\u0103 c\u0103 va fi procesat; pentru al\u021bii, un email invalid, dar comanda va fi totu\u0219i procesat\u0103, deoarece exist\u0103 un telefon de contact, iar al\u021bii vor spune c\u0103 o astfel de comand\u0103 nu va fi procesat\u0103 deloc.<\/p>\n<h3>Flux de date<\/h3>\n<p>\n\u00cen cazul lui Kafka se ridic\u0103 \u00eentrebarea organiz\u0103rii fluxului de date. Aceast\u0103 sarcin\u0103 este legat\u0103 de alegerea unei strategii pe c\u00e2teva puncte, s\u0103 le parcurgem pe toate.<\/p>\n<h4>\u00centr-un topic sau \u00een diferite?<\/h4>\n<p>\nAvem o specifica\u021bie a evenimentului. \u00cen BOB scriem c\u0103 o anumit\u0103 comand\u0103 trebuie livrat\u0103 \u0219i specific\u0103m: num\u0103rul comenzii, compunerea acesteia, anumite SKU-uri \u0219i coduri de bare etc. C\u00e2nd bunul ajunge la depozit, livrarea va putea primi st\u0103ri, timestamps \u0219i tot ce este necesar. Dar mai departe dorim s\u0103 primim actualiz\u0103ri despre aceste date \u00een BOB. Avem un proces invers de primire a datelor din livrare. Este acela\u0219i eveniment? Sau este o schimbare separat\u0103, care merit\u0103 un topic separat?<\/p>\n<p>Cel mai probabil, ele vor fi foarte asem\u0103n\u0103toare, iar tenta\u021bia de a face un topic unic nu este nejustificat\u0103, deoarece un topic separat \u00eenseamn\u0103 consumatori separa\u021bi, configura\u021bii separate, generarea separat\u0103 a tuturor acestora. Dar nu este sigur.<\/p>\n<h4>C\u00e2mp nou sau eveniment nou?<\/h4>\n<p>\nDar dac\u0103 folosim acelea\u0219i evenimente, apare o alt\u0103 problem\u0103. De exemplu, nu toate sistemele de livrare pot genera un astfel de DTO care s\u0103 poat\u0103 genera BOB. Noi le trimitem ID-ul, iar ei nu-l p\u0103streaz\u0103, pentru c\u0103 nu \u00eel consider\u0103 necesar, iar din perspectiva ini\u021bierii procesului event-bus, acest c\u00e2mp este obligatoriu. <\/p>\n<p>Dac\u0103 stabilim pentru event-bus regula c\u0103 acest c\u00e2mp este obligatoriu, atunci suntem nevoi\u021bi s\u0103 ad\u0103ug\u0103m reguli suplimentare de validare \u00een BOB sau \u00een handler-ul evenimentului ini\u021bial. Validarea \u00eencepe s\u0103 se r\u0103sp\u00e2ndeasc\u0103 \u00een serviciu - ceea ce nu este foarte convenabil.<\/p>\n<p>O alt\u0103 problem\u0103 este tenta\u021bia dezvolt\u0103rii incrementale. Ni se spune c\u0103 trebuie s\u0103 ad\u0103ug\u0103m ceva \u00een eveniment \u0219i, poate, dac\u0103 ne g\u00e2ndim bine, ar fi trebuit s\u0103 fie un eveniment separat. Dar \u00een schema noastr\u0103, un eveniment separat reprezint\u0103 un topic separat. Un topic separat \u00eenseamn\u0103 \u00eentregul proces pe care l-am descris mai sus. Dezvoltatorul are tendin\u021ba de a introduce pur \u0219i simplu un alt c\u00e2mp \u00een schema JSON \u0219i de a regenera.<\/p>\n<p>\u00cen cazul refund-urilor, astfel am ajuns \u00een \u0219ase luni la evenimentul evenimentelor. Am avut un metaeveniment numit refund update, care avea un c\u00e2mp type, descriind \u00een ce const\u0103, de fapt, acest update. De aici am avut \"minunate\" switch-uri cu validatori care spuneau cum trebuie validat acest eveniment cu acest type.<\/p>\n<h4>Versionarea evenimentelor<\/h4>\n<p>\nPentru validarea mesajelor \u00een Kafka se poate folosi <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, dar trebuia de la \u00eenceput s\u0103 integr\u0103m acest lucru \u0219i s\u0103 folosim Confluent. \u00cen cazul nostru cu versionarea, trebuie s\u0103 fim pruden\u021bi. Nu va fi \u00eentotdeauna posibil s\u0103 recitim mesajele din replication log, deoarece modelul \"s-a dus\". \u00cen principal, trebuie s\u0103 construim versiuni astfel \u00eenc\u00e2t modelul s\u0103 fie compatibil \u00eenapoi: de exemplu, s\u0103 facem c\u00e2mpul temporar op\u021bional. Dac\u0103 diferen\u021bele sunt prea mari, \u00eencepem s\u0103 scriem \u00eentr-un topic nou, iar clien\u021bii se mut\u0103 atunci c\u00e2nd termin\u0103 de citit vechiul.<\/p>\n<h4>Garan\u021bia ordinii de citire a partition-urilor<\/h4>\n<p>\nTopicurile din Kafka sunt \u00eemp\u0103r\u021bite \u00een partition-uri. Acest aspect nu este foarte important \u00een timp ce proiect\u0103m entit\u0103\u021bile \u0219i schimburile, dar devine relevant atunci c\u00e2nd decid\u0103m cum s\u0103 le consum\u0103m \u0219i s\u0103 scal\u0103m.<\/p>\n<p>\u00cen mod obi\u0219nuit, scrie\u021bi \u00eentr-un singur topic \u00een Kafka. Implicit, se folose\u0219te o singur\u0103 partition, iar toate mesajele acestui topic ajung \u00een ea. Iar consumatorul cite\u0219te aceste mesaje \u00een mod secven\u021bial. S\u0103 presupunem c\u0103 acum trebuie s\u0103 extindem sistemul astfel \u00eenc\u00e2t mesajele s\u0103 fie citite de doi consumatori diferi\u021bi. Dac\u0103, de exemplu, trimite\u021bi un SMS, se poate spune c\u0103 Kafka trebuie s\u0103 fac\u0103 o partition suplimentar\u0103, iar Kafka va \u00eencepe s\u0103 distribuie mesajele \u00een dou\u0103 p\u0103r\u021bi - jum\u0103tate acolo, jum\u0103tate aici. <\/p>\n<p>Cum le \u00eemparte Kafka? Fiecare mesaj are un corp (\u00een care stoc\u0103m JSON) \u0219i are o cheie. La aceast\u0103 cheie se poate aplica o func\u021bie hash, care va determina \u00een ce partition va ajunge mesajul.<\/p>\n<p>\u00cen cazul nostru cu refunds, acest lucru este important; dac\u0103 lu\u0103m dou\u0103 partitions, exist\u0103 \u0219ansa ca un consumator paralel s\u0103 proceseze al doilea eveniment \u00eenainte de primul, \u0219i va fi o problem\u0103. Func\u021bia hash garanteaz\u0103 c\u0103 mesajele cu aceea\u0219i cheie ajung \u00een aceea\u0219i partition. <\/p>\n<h4>Evenimente vs comenzi<\/h4>\n<p>\nAceasta este o alt\u0103 problem\u0103 cu care ne-am confruntat. Un eveniment este un anumit incident: spunem c\u0103 ceva s-a \u00eent\u00e2mplat (something_happened), de exemplu, un item a fost anulat sau a avut loc un refund. Dac\u0103 aceste evenimente sunt ascultate de cineva, atunci pentru \u00abitem anulat\u00bb entitatea refund va fi creat\u0103, iar \u00aba avut loc un refund\u00bb va fi \u00eenregistrat undeva \u00een set\u0103ri.<\/p>\n<p>Dar de obicei, atunci c\u00e2nd proiecta\u021bi evenimente, nu dori\u021bi s\u0103 le scrie\u021bi \u00een zadar - v\u0103 baza\u021bi pe faptul c\u0103 cineva le va citi. Exist\u0103 o mare tenta\u021bie s\u0103 nu scrie\u021bi something_happened (item_canceled, refund_refunded), ci something_should_be_done. De exemplu, itemul este gata pentru returnare.<\/p>\n<p>Pe de o parte, aceasta sugereaz\u0103 cum va fi folosit evenimentul. Pe de alt\u0103 parte, aceasta nu mai seam\u0103n\u0103 deloc cu un nume normal de eveniment. \u00cen plus, de aici nu este departe de comanda do_something. Dar nu ave\u021bi nicio garan\u021bie c\u0103 acest eveniment a fost citit de cineva; iar dac\u0103 a fost citit, atunci a fost citit cu succes; iar dac\u0103 a fost citit cu succes, \u00eenseamn\u0103 c\u0103 s-a f\u0103cut ceva, \u0219i acel ceva a trecut cu succes. \u00cen momentul \u00een care evenimentul devine do_something, devine necesar\u0103 o reac\u021bie, \u0219i aceasta este o problem\u0103.<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u00cen schimbul asincron \u00een RabbitMQ, c\u00e2nd a\u021bi citit un mesaj, a\u021bi mers pe http, ave\u021bi un r\u0103spuns - cel pu\u021bin c\u0103 mesajul a fost acceptat. C\u00e2nd a\u021bi scris \u00een Kafka, exist\u0103 un mesaj c\u0103 a\u021bi scris \u00een Kafka, dar nu \u0219ti\u021bi nimic despre cum a fost procesat. <\/p>\n<p>Prin urmare, \u00een cazul nostru a fost necesar s\u0103 introducem un eveniment de r\u0103spuns \u0219i s\u0103 configur\u0103m monitorizarea astfel \u00eenc\u00e2t, dac\u0103 s-au generat un anumit num\u0103r de evenimente, \u00eentr-un anumit interval de timp s\u0103 vin\u0103 tot at\u00e2tea evenimente de r\u0103spuns. Dac\u0103 acest lucru nu s-a \u00eent\u00e2mplat, \u00eenseamn\u0103 c\u0103 ceva a mers prost. De exemplu, dac\u0103 am trimis evenimentul \u201eitem_ready_to_refund\u201d, ne a\u0219tept\u0103m ca refund-ul s\u0103 fie creat, clientul s\u0103 primeasc\u0103 banii \u00eenapoi, iar noi s\u0103 primim evenimentul \u201emoney_refunded\u201d. Dar nu este niciodat\u0103 sigur, de aceea este necesar\u0103 monitorizarea.<\/p>\n<h3>Nuante<\/h3>\n<p>\nExist\u0103 o problem\u0103 destul de evident\u0103: dac\u0103 citi\u021bi din topic \u00een mod secven\u021bial, iar un mesaj este defect, consumatorul se opre\u0219te \u0219i nu ve\u021bi putea continua. Trebuie s\u0103 <strong>stopa\u021bi to\u021bi consumatorii<\/strong>, s\u0103 comite\u021bi offset-ul mai departe pentru a continua citirea.<\/p>\n<p>\u0218tiam despre asta, ne-am preg\u0103tit pentru aceast\u0103 situa\u021bie, \u0219i totu\u0219i s-a \u00eent\u00e2mplat. S-a \u00eent\u00e2mplat deoarece evenimentul a fost valid din punct de vedere al events-bus-ului, evenimentul a fost valid din punct de vedere al validatorului aplica\u021biei, dar nu a fost valid din punct de vedere al PostgreSQL-ului, deoarece \u00eentr-un sistem aveam MySQL cu UNSIGNED INT, iar \u00een sistemul recent scris aveam PostgreSQL doar cu INT. Dimensiunea lui este pu\u021bin mai mic\u0103, iar Id-ul nu a \u00eenc\u0103put. Symfony a picat cu o excep\u021bie. Sigur c\u0103 am prins excep\u021bia, deoarece ne-am preg\u0103tit pentru ea \u0219i ne-am propus s\u0103 comitem acest offset, dar \u00eenainte de asta am dorit s\u0103 increment\u0103m contorul problemelor, av\u00e2nd \u00een vedere c\u0103 mesajul a fost procesat cu e\u0219ec. Contoarele din acest proiect sunt de asemenea stocate \u00een baza de date, iar Symfony a \u00eenchis deja comunicarea cu baza de date, \u0219i a doua excep\u021bie a distrus \u00eentregul proces f\u0103r\u0103 \u0219anse de a comite offset-ul.<\/p>\n<p>Serviciul a stat a\u0219a un timp \u2014 din fericire, cu Kafka nu este at\u00e2t de grav, deoarece mesajele r\u0103m\u00e2n. C\u00e2nd activitatea se va restabili, se vor putea citi. Este convenabil.<\/p>\n<p>Kafka are op\u021biunea de a fixa un offset la alegere prin uneltele de lucru. Dar pentru a face acest lucru, trebuie s\u0103 opri\u021bi to\u021bi consumatorii \u2014 \u00een cazul nostru s\u0103 preg\u0103tim o versiune separat\u0103 \u00een care nu vor fi consumatori, redeployments. Atunci, prin uneltele de lucru, se poate ajusta offset-ul \u00een Kafka \u0219i mesajul va trece.<\/p>\n<p>O alt\u0103 nuan\u021b\u0103 \u2014 <strong>log de replicare vs rdkafka.so<\/strong>\u00a0\u2014 este legat de specificitatea proiectului nostru. Avem PHP, iar \u00een PHP, de obicei, toate bibliotecile comunic\u0103 cu Kafka prin intermediul depozitului rdkafka.so, iar mai departe intervine un fel de wrapper. Poate c\u0103 sunt dificult\u0103\u021bi personale, dar s-a dovedit c\u0103 a reciti o parte dintr-un text deja parcurs nu este tocmai simplu. \u00cen general, au fost probleme de programare.<\/p>\n<p>Revenind la particularit\u0103\u021bile lucrului cu partitions, este scris clar \u00een documenta\u021bie <strong>consumers &gt;= topic partitions<\/strong>. Dar am aflat despre asta mult mai t\u00e2rziu dec\u00e2t mi-a\u0219 fi dorit. Dac\u0103 dori\u021bi s\u0103 scala\u021bi \u0219i s\u0103 ave\u021bi doi consumatori, ave\u021bi nevoie de cel pu\u021bin dou\u0103 partitions. Asta \u00eenseamn\u0103 c\u0103, dac\u0103 a\u021bi avut un singur partition \u00een care s-au acumulat 20 de mii de mesaje, iar acum a\u021bi f\u0103cut unul nou, num\u0103rul mesajelor nu se va echilibra prea cur\u00e2nd. A\u0219a c\u0103, pentru a avea doi consumatori paraleli, trebuie s\u0103 v\u0103 ocupa\u021bi de partitions.<\/p>\n<h2>Monitorizare<\/h2>\n<p>\nCred c\u0103, din modul \u00een care monitoriz\u0103m, va fi \u0219i mai clar ce probleme exist\u0103 \u00een abordarea actual\u0103.<\/p>\n<p>De exemplu, num\u0103r\u0103m c\u00e2te produse din baza de date \u0219i-au schimbat statusul recent, \u0219i, prin urmare, pe baza acestor schimb\u0103ri ar fi trebuit s\u0103 aib\u0103 loc evenimente, \u0219i trimitem acest num\u0103r \u00een sistemul nostru de monitorizare. Apoi, din Kafka ob\u021binem al doilea num\u0103r, c\u00e2te evenimente au fost de fapt \u00eenregistrate. Este evident c\u0103 diferen\u021ba dintre aceste dou\u0103 numere ar trebui s\u0103 fie \u00eentotdeauna zero.<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u00cen plus, trebuie s\u0103 monitoriz\u0103m cum stau lucrurile cu produc\u0103torul, dac\u0103 events-bus a primit mesajele, \u0219i cum stau lucrurile cu consumatorul. De exemplu, \u00een graficele de mai jos, la Refund Tool totul este bine, iar la BOB sunt evident unele probleme (v\u00e2rfuri albastre).<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAm mai men\u021bionat \u00eent\u00e2rzierile grupului de consumatori. \u00cen termeni simpli, acesta este num\u0103rul de mesaje ne\u00eenc\u0103rcate. \u00cen general, consumatorii no\u0219tri func\u021bioneaz\u0103 repede, a\u0219a c\u0103 \u00eent\u00e2rzierile sunt de obicei 0, dar uneori poate ap\u0103rea un v\u00e2rf temporar. Kafka gestioneaz\u0103 acest lucru din cutie, dar trebuie s\u0103 impune\u021bi un anumit interval. <\/p>\n<p>Exist\u0103 un proiect <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, care v\u0103 va oferi mai multe informa\u021bii despre Kafka. Acesta returneaz\u0103 pur \u0219i simplu, prin API, statutul grupului de consumatori, cum stau lucrurile cu acest grup. \u00cen afar\u0103 de OK \u0219i Failed, exist\u0103 \u0219i warning, \u0219i ve\u021bi putea afla c\u0103 consumatorii vo\u0219tri nu fac fa\u021b\u0103 ritmului de produc\u021bie \u2014 nu reu\u0219esc s\u0103 citeasc\u0103 ceea ce se scrie. Sistemul este destul de inteligent, este convenabil de utilizat. <\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/cece8495801e187b155487a802e1a35a.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nA\u0219a arat\u0103 r\u0103spunsul prin API. Aici grupul bob-live-fifa, partition refund.update.v1, statut OK, lag 0 \u2014 ultimul offset final este acesta.<\/p>\n<p><img decoding=\"async\" alt=\"Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMonitorizare <strong>updated_at SLA (stuck)<\/strong> Am men\u021bionat deja. De exemplu, un produs a trecut \u00een statutul de preg\u0103tit pentru returnare. Set\u0103m un Cron care indic\u0103 c\u0103, dac\u0103 \u00een 5 minute obiectul respectiv nu a trecut \u00een statutul de refund (noi return\u0103m banii foarte rapid prin sistemele de plat\u0103), atunci ceva s-a \u00eent\u00e2mplat cu siguran\u021b\u0103 gre\u0219it \u0219i acesta este cu siguran\u021b\u0103 un caz pentru suport. A\u0219adar, folosim un Cron care cite\u0219te astfel de situa\u021bii, iar dac\u0103 acestea sunt mai mari dec\u00e2t 0, trimite un alert.<\/p>\n<p><b>\u00cen concluzie, a folosi evenimentele este convenabil atunci c\u00e2nd<\/b>:<\/p>\n<ul>\n<li>informa\u021bia este necesar\u0103 pentru mai multe sisteme;<\/li>\n<li>rezultatul proces\u0103rii nu este important;<\/li>\n<li>sunt pu\u021bine evenimente sau evenimentele sunt mici. <\/li>\n<\/ul>\n<blockquote><p>Ar p\u0103rea c\u0103 articolul are un subiect foarte specific - API asincron pe Kafka, dar \u00een leg\u0103tur\u0103 cu acesta vreau s\u0103 recomand multe lucruri.<br \/>\n\u00cen primul r\u00e2nd, urm\u0103torul <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> nu trebuie s\u0103 a\u0219tept\u0103m p\u00e2n\u0103 \u00een noiembrie, deja \u00een aprilie va fi versiunea sa din St. Petersburg, iar \u00een iunie vom discuta despre sarcini mari \u00een Novosibirsk.<br \/>\n\u00cen al doilea r\u00e2nd, autorul raportului, Serghei Zaika, face parte din Comitetul de Program al noii noastre conferin\u021be despre managementul cuno\u0219tin\u021belor <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>. Conferin\u021ba va fi de o zi, are loc pe 26 aprilie, dar programul s\u0103u este foarte bogat.<br \/>\n\u0218i de asemenea, \u00een mai va avea loc <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russia<\/a><\/noindex> \u0219i\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> (cu DevOpsConf inclus) - acolo se pot propune \u00eenc\u0103 teme, pentru a \u00eemp\u0103rt\u0103\u0219i experien\u021ba proprie \u0219i a ne pl\u00e2nge de loviturile primite.<\/p><\/blockquote>\n<p>Sursa: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/oleg-bunin\/blog\/445424\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441\u00a0\u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438\u00a0\u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442\u00a0\u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u00a0\u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e\u00a0\u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435\u00a0\u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u00a0\u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412\u00a0\u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430\u00a0Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438\u00a0\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":22763,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-30778","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434?\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041e\u043f\u044b\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Refund Tool \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c API \u043d\u0430 Kafka | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434?\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:37:23+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:37:23+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Experien\u021ba dezvolt\u0103rii serviciului Refund Tool cu API asincron pe Kafka | ProHoster","description":"Ce ar putea determina o companie at\u00e2t de mare precum Lamoda, cu un proces bine definit \u0219i zeci de servicii interconectate, s\u0103-\u0219i schimbe semnificativ abordarea?","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041e\u043f\u044b\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Refund Tool \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c API \u043d\u0430 Kafka | ProHoster","og:description":"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434?","og:url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:37:23+00:00","article:modified_time":"2019-10-31T18:37:23+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"30778","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 02:58:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 03:29:56","updated":"2026-01-21 02:58:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}