Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Çfarë mund ta bëjë një kompani kaq të madhe si Lamoda, e cila ka një proces të organizuar dhe dhjetëra shërbime të ndërlidhura, të ndryshojë ndjeshëm qasjen e saj? Motivimi mund të jetë krejt ndryshe: nga legjislative deri te dëshira për të eksperimentuar që kanë të gjithë programuesit.

Por kjo nuk do të thotë se nuk mund të pritet një përfitim shtesë. Çfarë konkretisht mund të fitohet nëse implementohet një API i bazuar në ngjarje në Kafka, do të tregojë Sergey Zaika (fewald). Rreth gafeve dhe zbulimeve interesante gjithashtu do të ketë patjetër - nuk mund të ketë eksperiment pa to.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Disclaimer: Ky artikull bazohet në materialet e mitapit që Sergey mbajti në nëntor 2018 në HighLoad++. Eksperienca për Lamoda me Kafka tërhoqi dëgjuesit jo më pak se prezantimet e tjera nga programi. Na duket se kjo është një shembull i shkëlqyer se gjithmonë mund dhe duhet të gjejmë mendimtarë të ngjashëm, dhe organizatorët e HighLoad++ do të vazhdojnë të krijojnë një atmosferë që inkurajon këtë.

Rreth procesit

Lamoda — është një platformë e madhe e-commerce, e cila ka qendrën e saj të kontakteve, shërbimin e dorëzimit (dhe shumë partnerë), studio fotografike, një magazinë të madhe dhe gjithçka funksionon me softin e saj. Ka dhjetëra mënyra pagesash, partnerë B2B që mund të përdorin disa ose të gjitha këto shërbime dhe duan të dinë informacionin më të fundit për produktet e tyre. Për më tepër, Lamoda operon në tri vende përveç RF dhe atje gjithçka është pak ndryshe. Pra, ndoshta ekzistojnë më shumë se njëqind mënyra për të konfiguruar një porosi të re, e cila duhet të përpunojë ndryshe. E gjithë kjo funksionon me ndihmën e dhjetëra shërbimeve, të cilat ndonjëherë komunikojnë në mënyra jo të qarta. Ka edhe një sistem qendror, përgjegjësia kryesore e së cilës janë statuset e porosive. Ne e quajmë atë BOB, unë punoj me të.

Refund Tool me API të ngacmuar nga ngjarjet

Fjala ngacmuar nga ngjarjet është mjaft e përdorur, më tutje do të përcaktojmë në detaje se çfarë nënkupton kjo. Do ta filloj me kontekstin në të cilin vendosëm të provojmë qasjen API të ngacmuar nga ngjarjet në Kafka.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Në çdo dyqan, përveç porosive për të cilat klientët paguajnë, ka raste kur dyqani kërkohet të kthejë para, sepse produkti nuk i përshtatet klientit. Ky është një proces relativisht i shkurtër: sqarojmë informacionin, nëse është e nevojshme, dhe transferojmë paratë.

Por, kthimi u komplikuar për shkak të ndryshimit të legjislacionit, dhe na u desh të realizonim një mikroshërbim të veçantë për këtë.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Motivimi ynë:

  1. Ligji FZ-54 — në shkurt, ligji kërkon që të raportohet në tatim mbi çdo operacion financiar, qoftë kthim, qoftë pranimi, në një SLA mjaft të shkurtër prej disa minutash. Ne, si e-commerce, realizojmë shumë operacione. Kjo teknike do të thotë një përgjegjësi të re (dhe pra një shërbim të ri) dhe përmirësime në të gjitha sistemet e përfshira.
  2. BOB split — një projekt i brendshëm i kompanisë për të eliminuar një numër të madh përgjegjësish të pa profilizuara nga BOB dhe për të reduktuar kompleksitetin e tij të përgjithshëm.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Në këtë skemë janë skicuar sistemet kryesore të Lamoda. Tani për tani, shumica e tyre përbëjnë më tepër një grup prej 5-10 mikroshërbimesh rreth një monoliti në zvogëlim.. Ato po ngjashen, por përpiqemi t'i bëjmë ato më të vogla, sepse është e frikshme të krijosh një pjesë të dedikuar në mes — nuk mund të lejojmë që ajo të bie. Të gjitha shkëmbimet (shigjetat) jemi të detyruar t'i rezervojmë dhe të llogarisim se çfarëdo prej tyre mund të jetë e paperceptueshme.

Në BOB ka gjithashtu mjaft shkëmbime: sisteme pagesash, dorëzimesh, njoftimesh, etj.

Teknikisht, BOB është:

  • ~150k rreshta kode + ~100k rreshta teste;
  • php7.2 + Zend 1 & Symfony Components 3;
  • >100 API & ~50 integrime të dala;
  • 4 vende me logjikën e tyre biznesore.

Deployimi i BOB-së është i kushtueshëm dhe i dhimbshëm, numri i kodeve dhe detyrave që ai zgjidh është i tillë sa askush nuk mund ta mbahet atë në mend tërë.

Procesi i kthimit

Fillimisht, në proces përfshihen dy sisteme: BOB dhe Pagesa. Tani shfaqen edhe dy të tjera:

  • Shërbimi i Fiskalizimit, i cili do të marrë përsipër problemet me fiskalizimin dhe komunikimin me shërbime të jashtme.
  • Mjeti i Kthimit, në të cilin thjesht përfshihen shkëmbimet e reja, për të mos e fryrë BOB.

Tani procesi duket kështu:

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

  1. BOB merr një kërkesë për kthimin e parave.
  2. BOB e njofton këtë Mjetin e Kthimit.
  3. Mjeti i Kthimit i thotë Pagesës: «Kthe paratë».
  4. Pagesa kthen paratë.
  5. Refund Tool dhe BOB sinkronizojnë statuset e tyre, sepse aktualisht janë të dy të nevojshëm. Nuk jemi ende të gatshëm të kalojmë plotësisht në Refund Tool, pasi në BOB ka UI, raporte për kontabilitetin, dhe gjithashtu shumë të dhëna që nuk janë kaq lehtë për t'u transferuar. Na duhet të qëndrojmë në dy karrige.
  6. Po dërgohet kërkesa për fiskalizim.

Si rezultat, bëmë një bus ngjarjesh në Kafka — event-bus, në të cilin gjithçka u lidh. Hurrah, tani kemi një pikë të vetme dështimi (sarcastik).

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Pikat e forta dhe të dobëta janë mjaft të qarta. Krijuam një bus, që do të thotë se tani të gjithë shërbimet varen nga ajo. Kjo e thjeshton projektimin, por sjell një pikë të vetme dështimi në sistem. Nëse Kafka bie, procesi do të ndalojë.

Çfarë është API me ngjarje?

Një përgjigje e mirë për këtë pyetje është në raportin e Martin Fowler (GOTO 2017) "The Many Meanings of Event-Driven Architecture".

Përmbledhje e asaj që bëmë:

  1. Ambalazhuam të gjithë shkëmbimet asinkrone përmes storage të ngjarjeve. Në vend që të njoftojmë në rrjet çdo konsumator të interesuar për ndryshimin e statusit, ne shkruajmë në një depo të centralizuar një ngjarje për ndryshimin e gjendjes, dhe konsumatorët e interesuar në temë e lexojnë prej aty gjithçka që shfaqet.
  2. Një ngjarje (event) në këtë rast është një njoftim (njoftimet) që diçka ka ndryshuar diku. Për shembull, ka ndryshuar statusi i porosisë. Konsumatori, të cilit i interesojnë disa të dhëna shoqëruese të informacionit të statusit dhe që nuk janë në njoftim, mund të mësojë vetë gjendjen e tyre.
  3. Alternativa maksimale është një sourcing ngjarjesh të plotë, transferimi i gjendjes, ku ngjarja përmban të gjithë informacionin e nevojshëm për përpunim: nga e ku e kaloi statusin dhe si ndryshuan të dhënat etj. Çështja është vetëm në racionalitetin dhe sasinë e informacionit që mund të lejoni të ruhet.

Në kuadër të lançimit të Refund Tool kemi përdorur variantin e tretë. Kjo e ka thjeshtuar përpunimin e ngjarjeve, pasi nuk ka nevojë për të nxjerrë informacion të detajuar, për më tepër, përjashton skenarin ku çdo ngjarje e re shkakton një shpërthim të kërkesave për sqarime nga konsumatorët.

Shërbimi Refund Tool nuk është i ngarkuar, kështu që Kafka atje është më shumë një provë se një nevojë. Nuk mendoj se, nëse shërbimi i rikthimit të fondeve do të bëhej një projekt me ngarkesë të lartë, biznesi do të ishte i lumtur.

Shkëmbimi asinkron AS IS

Për shkëmbimet asinkrone, departamenti i PHP zakonisht përdor RabbitMQ. Kemi mbledhur të dhënat për kërkesën, i vendosëm në radhë, dhe konsumatori i këtij shërbimi e lexoi dhe e dërgoi (ose nuk e dërgoi). Për API-në e saj, Lamoda e përdor aktivisht Swagger. Projektojmë API-në, e përshkruajmë atë në Swagger, gjenerojmë kodin për klientët dhe serverët. Ne gjithashtu përdorim një JSON RPC 2.0 të zgjeruar.

Disa vende përdorin bus-esb, dikush jeton me activeMQ, por, në përgjithësi, RabbitMQ është standardi.

Shkëmbim asinkron DO TË JETË

Kur projektojmë shkëmbimin përmes events-bus, ka një analogji. Ne përshkruajmë në mënyrë të ngjashme shkëmbimin e të dhënave të ardhshëm përmes përshkrimeve të strukturës së event-it. Formati është yaml, dhe na duhej të bënim vetë gjenerimin e kodit, gjeneratori sipas specifikimit krijon DTO dhe i mëson klientët dhe serverët të punojnë me ta. Gjenerimi shkon në dy gjuhë - golang dhe php. Kjo lejon që bibliotekat të mbahen të sinkronizuara. Gjeneratori është shkruar në golang, për të cilin mori emrin gogi.

Event-sourcing në Kafka është një gjë tipike. Ekziston një zgjidhje nga versioni kryesor i Kafka Confluent, ka nakadi, një zgjidhje nga 'vëllezërit' tanë në fushën e domenit Zalando. Moti motivi ynë për të filluar me vanilla Kafka — është ta mbani zgjidhjen falas, derisa të vendosim nëse do ta përdorim atë gjithandej, si dhe të mbajmë hapësirë për manovra dhe përmirësime: ne dëshirojmë mbështetje për të. JSON RPC 2.0, gjeneratorë për dy gjuhë dhe do të shohim se çfarë tjetër.

Ironike është që, edhe në një rast kaq të lumtur, kur ka një biznes të ngjashëm ndaj Zalando, i cili bëri një zgjidhje mjaft të ngjashme, nuk mund ta përdorim atë në mënyrë efektive.

Arkitektonikisht, në lançim pattern-i është i tillë: lexojmë drejtpërdrejt nga Kafka, por shkruajmë vetëm përmes events-bus. Për të lexuar në Kafka ka shumë të gatshme: brokerë, balancerë dhe është më shumë-më pak e gatshme për të skalator horizontalisht, kjo donim ta mbaja. Ndërsa për shkrimin, ne do të donim ta paketojmë përmes një Gateway aka Events-bus, dhe ja përse.

Events-bus

Ose autobusi i ngjarjeve. Është thjesht një gateway http pa shtet, i cili merr mbi vete disa role të rëndësishme:

  • Validimi i prodhimit — kontrollojmë që ngjarjet përputhen me specifikimin tonë.
  • Sistemi kryesor për ngjarjet, që do të thotë kjo është sistemi kryesor dhe i vetëm në kompani, i cili përgjigjet në pyetjen se cilat ngjarje me cilat struktura konsiderohen si të vlefshme. Validimi përfshin vetëm llojet e të dhënave dhe enums për specifikimin e rreptë të përmbajtjes.
  • Funksioni Hash për sharding - struktura e mesazhit Kafka është key-value dhe këtu sipas hashes nga key, llogaritet se ku duhet ta vendosim këtë.

Pse

Ne punojmë në një kompani të madhe me një proces të organizuar. Pse të ndryshojmë diçka? Ky është një eksperiment, dhe ne presim të fitojmë disa avantazhe.

1:n+1 shkëmbime (një me shumë)

Me Kafka është shumë e lehtë të lidheni me API-të e konsumatorëve të rinj.

Supozoni se keni një regjistër që duhet të mbahet i azhurnuar në disa sisteme njëherësh (dhe në disa të reja). Më parë ne shpiknim një bundle që realizonte set-API, dhe sistemit master i raportonim adresat e konsumatorëve. Tani sistemi master dërgon azhurnime në një topic, dhe të gjithë ata që janë të interesuar lexojnë. Pati një sistem të ri - e regjistruam atë në topic. Po, gjithashtu bundle, por më e thjeshtë.

Në rastin e refund-tool, që është një copëz BOB, na përshtatet të mbajmë ato të sinkronizuara përmes Kafka. Pagesa thotë se paratë janë rikthyer: BOB, RT janë njoftuar, kanë ndryshuar statuset e tyre, Shërbimi i Fikës ka marrë lajmin dhe ka lëshuar faturën.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Kemi plane për të krijuar një Shërbim Njoftimesh të njëjtë, që do të informonte klientin për lajmet në lidhje me porosinë e tij/rikthimet. Tani, kjo përgjegjësi është e shpërndarë mes sistemeve. Na mjafton të mësojmë Shërbimin e Njoftimeve të kapë informacione relevante nga Kafka dhe të reagojë ndaj tyre (dhe të fikim këto njoftime në sistemet e tjera). Nuk do të nevojiten ndërrime të reja të drejtpërdrejta.

Të dhënat e drejtuara nga të dhënat

Informacioni mes sistemeve bëhet i qartë — çfarëdo «enterprise-i të dhimbshëm» që keni dhe sa i mëdha të jetë backlog-u juaj. Në Lamoda, ekziston një departament i Analitikës së të Dhënave, që mbledh të dhëna nga sistemet dhe i sjell ato në një formë të ripërdorshme, si për biznesin ashtu edhe për sistemet inteligjente. Kafka lejon të jepni shpejt shumë të dhëna dhe të mbani këtë shpërndarje informacioni të azhurnuar.

Dita e replikimit

Mesazhet nuk shpërbëhen pas leximit, si në RabbitMQ. Kur ngjarja përmban mjaft informacion për përpunim, ne kemi një histori të ndryshimeve të fundit për objektin dhe, nëse dëshirohet, mundësinë për t'i aplikuar këto ndryshime.

Koha e ruajtjes së replikimit varet nga intensiteti i shkrimit në këtë temë; Kafka lejon të përshtaten fleksibël kufijtë mbi kohën e ruajtjes dhe mbi volumin e të dhënave. Për temat me intensitet të lartë, është e rëndësishme që të gjithë konsumatorët të arrijnë të lexojnë informacionin përpara se ai të zhduket, madje edhe në rast të një mosfunksionimi të përkohshëm. Zakonisht, është e mundur të ruhet informacioni për njësi ditësh, që është mjaft e mjaftueshme për mbështetje.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Më pas, pak përmbledhje e dokumentacionit për ata që nuk janë të njohur me Kafka (imazhi gjithashtu nga dokumentacioni)

Në AMQP ka radhë: shkruajmë mesazhe në radhë për konsumatorin. Zakonisht, një radhë përpunon një sistem me një logjikë biznesi të njëjtë. Nëse duam të njoftojmë disa sisteme, mund ta mësojmë aplikacionin të shkruajë në disa radhë ose të konfiguroni exchange me mekanizmin fanout, i cili vetë i klonon ato.

Në Kafka ka një abstraksion të ngjashëm temë, në të cilin shkruani mesazhe, por ato nuk zhduken pas leximit. Në mënyrë të paracaktuar, kur lidheni me Kafka, merrni të gjitha mesazhet dhe keni mundësinë ta ruani vendin ku keni ndaluar. Domethënë, i lexoni ato në mënyrë të njëpasnjëshme, nuk është e nevojshme të shënohet mesazhi si të lexuar, por mund ta ruani id, nga e cila do të vazhdoni më pas me leximin. Id, në të cilën keni ndaluar, quhet offset, ndërsa mekanizmi quhet commit offset.

Për pasojë, mund të realizojmë logjikë të ndryshme. Për shembull, kemi BOB-in që ekziston në 4 instanca për vende të ndryshme - Lamoda është në Rusi, Kazakistan, Ukrainë dhe Bjellorusi. Pasi që ato deplohen veçmas, kanë pak interpretime të ndryshme dhe logjikën e tyre të biznesit. Ne tregojmë në mesazh se për cilin vend është. Çdo konsumator BOB në çdo vend lexon me grupe të ndryshme ID dhe, nëse mesazhi nuk i përket atij, e kalon, dmth. menjëherë komiton offset +1. Nëse po, tema që lexon Shërbimi ynë i Pagesave, atëherë ai e bën këtë me një grup të veçantë, dhe për këtë arsye offset-et nuk preken.

Kërkesat për ngjarjet:

  • Plotësia e të dhënave. Do të doja që ngjarja të kishte të dhëna të mjaftueshme për t'u përpunuar.

  • Integriteti. Ne delegojmë Events-bus kontrollin e faktit që ngjarja është konsistente dhe se ai mund ta përpunojë atë.
  • Renditja është e rëndësishme. Në rastin e rikthimit, ne detyrohemi të punojmë me historinë. Me njoftimet rendi nuk është i rëndësishëm, nëse janë njoftime homogjene, emaili do të jetë i njëjtë pavarësisht nga porosia që ka mbërritur e para. Në rast rikthimi ka një proces të qartë; nëse ndryshojmë rendin, do të ndodhin përjashtime, rikthimi nuk do të krijohet ose nuk do të përpunohet - do të përfundojmë në një status tjetër.
  • Konsistenca. Ne kemi një depo, dhe tani ne krijojmë ngjarjet në vend të API-së. Na nevojitet një mënyrë për të kaluar shpejt dhe lirë informacionin për ngjarje të reja dhe për ndryshime në ato ekzistuese në shërbimet tona. Kjo arrihet përmes një specifikimi të përbashkët në një depo të veçantë git dhe gjeneratorëve të kodit. Prandaj, klientët dhe serverët në shërbime të ndryshme janë të harmonizuara.

Kafka në Lamoda

Ne kemi tre instalime të Kafka:

  1. Logs;
  2. R&D;
  3. Events-bus.

Sot kemi për të folur vetëm për pikën e fundit. Në events-bus ne nuk kemi instalime shumë të mëdha — 3 brokerë (servera) dhe vetëm 27 tema. Në përgjithësi, një temë është një proces. Por ky është një detaj delikat, dhe tani do ta prekemë atë.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Më lart është grafiku rps. Procesi i kthimeve shënohet me një vijë të ngjyrës së turkizit (po, ajo që ndodhet në boshtin X), ndërsa me ngjyrë rozë — procesi i azhurnimit të përmbajtjes.

Katalogu Lamoda përmban miliona produkte, dhe të dhënat përditësohen vazhdimisht. Disa koleksione dalin jashtë mode, ndërsa të reja lëshohen në vend të tyre, dhe në katalog vazhdimisht shfaqen modele të reja. Ne përpiqemi të parashikojmë se çfarë do t'i interesojë klientëve tanë nesër, prandaj vazhdimisht blejmë artikuj të rinj, i fotografomë dhe përditësojmë vitrinën.

Majat rozë janë azhurnime produktesh, që do të thotë ndryshime në produkte. Mund të shihet se ata kanë fotografuar, fotografuar, dhe pastaj bam! — ngarkuan një sërë ngjarjesh.

Rastet e përdorimit të Lamoda Events

Arkitekturën e ndërtuar e përdorim për operacione të tilla:

  • Ndjekja e statusit të kthimeve: thirrje për veprim dhe ndjekje e statusit nga të gjitha sistemet e angazhuara. Pagesa, statuset, fiskalizimi, njoftimet. Këtu kemi provuar një qasje, zhvilluar mjetet, mbledhur të gjitha defektet, shkruar dokumentacionin dhe treguar kolegëve se si të përdorin ato.
  • Përditësimi i kartelave të produkteve: konfigurimi, meta-të dhënat, karakteristikat. Një sistem lexon (që paraqitet), ndërsa disa shkruajnë.
  • Email, push dhe sms: porosia është mbledhur, porosia ka mbërritur, kthimi është pranuar, etj., shumë të tillë.
  • Stoku, përditësimi i magazinës — përditësimi sasiore i emrave, thjesht numra: ardhja në magazinë, kthimi. Nevojitet që të gjitha sistemet, të lidhura me rezervimin e produkteve, të operojnë me të dhëna sa më të sakta. Aktualisht, sistemi i përditësimit të stokut është mjaft kompleks, Kafka do ta thjeshtojë atë.
  • Analiza e të Dhënave (departamenti R&D), mjete ML, analiza, statistika. Ne duam që informacioni të jetë transparent — për këtë, Kafka është shumë e përshtatshme.

Tani pjesa më interesante rreth ngjarjeve të papritura dhe zbulimeve interesante që ndodhën për gjashtë muaj.

Problemet e dizajnimit

Supozoni se dëshirojmë të bëjmë një gjë të re — për shembull, të transferojmë të gjithë procesin e dorëzimit në Kafka. Tani një pjesë e procesit realizohet në Order Processing në BOB. Pas kalimit të porosisë në shërbimin e dërgesës, lëvizjes në magazinën ndërmjetëse dhe çështjeve të tjera qëndron një model statusi. Ka një monolit të tërë, madje dy, plus një mori API që i kushtohen dërgesës. Ata dinë shumë më tepër rreth dorëzimit.

Duket se janë fusha të ngjashme, por për Order Processing në BOB dhe për sistemin e dërgesës, statuset dallojnë. Për shembull, disa shërbime të kurierëve nuk dërgojnë statuset ndërmjetëse, por vetëm finale: "dërguar" ose "humbur". Të tjerat, përkundrazi, raportojnë shumë hollësisht për lëvizjen e mallrave. Të gjithë kanë rregulla të veta për verifikimin: për disa, një email i vlefshëm do të thotë që do të përpunojnë; për të tjerët — një email jo i vlefshëm, por porosia do të përpunojë për shkak se ka një telefon për kontakt, ndërsa disa do të thonë që një porosi e tillë nuk do të përpunohet fare.

Shkalla e të dhënave

Në rastin e Kafka-s, lind pyetja e organizimit të shkallës së të dhënave. Ky detyrë lidhet me zgjedhjen e strategjisë në disa pika, do të kalojmë nëpër to të gjitha.

Në një temë apo në të ndryshme?

Ne kemi një specifikim ngjarjeje. Në BOB ne shkruajmë se cila porosi duhet të dërgohet dhe tregojmë: numrin e porosisë, përbërjen e saj, disa SKU dhe barkodet, etj. Kur produkti të arrijë në magazinë, dërgesa do të mund të marrë statuse, timestamps dhe gjithçka që nevojitet. Por pastaj ne duam të marrim përditësime në BOB për këto të dhëna. Këtu krijohet një proces i kundërt për marrjen e të dhënave nga dërgesa. A është kjo e njëjta ngjarje? Ose është një shkëmbim i veçantë që meriton një temë të veçantë?

Me sa duket, ato do të jenë shumë të ngjashme dhe sugjerimi për të krijuar një temë të vetme është i arsyeshëm, sepse një temë e veçantë do të thotë konsumatorë të veçantë, konfigurime të veçanta, dhe gjenerimin e gjithçkaje të kësaj. Por nuk është e sigurt.

Fushë e re apo ngjarje e re?

Por nëse përdoren të njëjtat ngjarje, ndodh një problem tjetër. Për shembull, jo të gjitha sistemet e dërgesës mund të gjenerojnë një DTO të tillë që mund të gjenerojnë BOB. Ne iu dërgojmë atyre id, por ata nuk i ruajnë, sepse nuk u nevojiten, ndërsa nga pikëpamja e fillimit të procesit të event-bus, kjo fushë është e domosdoshme.

Nëse vendosim një rregull për event-bus që ky fushë është e detyrueshme, atëherë jemi të detyruar të vendosim rregulla shtesë verifikimi në BOB ose në përpunuesin e ngjarjeve fillestare. Verifikimi fillon të përhapet në shërbim — kjo nuk është shumë e përshtatshme.

Një tjetër problem është tundimi i zhvillimit inkremental. Na thonë se duhet të shtojmë diçka në ngjarje, dhe ndoshta, nëse mendoni mirë, kjo duhej të ishte një ngjarje e veçantë. Por në skemën tonë, një ngjarje e veçantë është një temë e veçantë. Një temë e veçantë është gjithë ai proces që e përshkrova më sipër. Zhvilluesi ka tundimin të thjesht të shtojë një fushë tjetër në skemën JSON dhe të gjenerojë përsëri.

Në rastin e refunds, ashtu arritëm pas gjashtë muajsh në ngjarjen e ngjarjeve. Kemi pasur një meta-ngjarje që quhet rifreskim i rimbursimit, e cila kishte një fushë tipi, duke përshkruar se në çfarë konsiston ky rifreskim. Nga kjo kishim 'fantastike' switch-e me verifikuesit, që tregonin si duhet të verifikohet kjo ngjarje me këtë tip.

Versionimi i ngjarjeve

Për verifikimin e mesazheve në Kafka, mund të përdorim Avro, por duhet te planifikohet qe të përdoret Confluent. Në rastin tonë me versionimin duhet të jemi të kujdesshëm. Nuk do të jetë gjithmonë e mundur të rivizitojmë mesazhet nga replication log, për shkak se modeli u zhvendos. Kryesisht, arrihet të ndërtojmë versione në mënyrë që modeli të jetë në përputhje prapavepruese: për shembull, ta bëjmë një fushë përkohësisht të panevojshme. Nëse dallimet janë shumë të mëdha, fillojmë të shkruajmë në një temë të re, dhe klientët migr përshtaten kur të përfundojnë leximin e atij të vjetri.

Garancia e rendit të leximit të partitions

Temat brenda Kafka janë të ndara në partitions. Kjo nuk është shumë e rëndësishme derisa të projektomë entitetet dhe shkëmbimet, por është e rëndësishme kur vendosim si t’i konsumohem dhe të shkallëzojmë.

Në gjendjen e zakonshme, ju shkruani një topic në Kafka. Nëse nuk ndryshohet, përdoret një partition dhe të gjitha mesazhet e atij topic-i shkojnë aty. Konsumatori pastaj lexon këto mesazhe një pas një. Le të themi tani se duhet të zgjeroni sistemin në mënyrë që mesazhet të lexohen nga dy konsumatorë të ndryshëm. Nëse ju, për shembull, dërgoni një SMS, mund të thoni se Kafka duhet të krijojë një partition të shtuar dhe Kafka do të fillojë të ndajë mesazhet në dy pjesë — gjysmën aty, gjysmën këtu.

Si i ndan Kafka ato? Çdo mesazh ka një trup (ku ruajmë JSON) dhe një çelës. Në këtë çelës mund të aplikoni një funksion hash, që do të përcaktojë se në cilin partition do të bjerë mesazhi.

Në rastin tonë me refunds, kjo është e rëndësishme; nëse marrim dy partition, ka një mundësi që konsumatori paralel të përpunojë ngjarjen e dytë përpara asaj të parë dhe kjo do të ishte një problem. Funksioni hash garanton që mesazhet me çelësa të njëjte do të bien në të njëjtin partition.

Ngjarjet vs urdhra

Kjo është një tjetër çështje me të cilën jemi përballur. Një ngjarje është një lloj ngjarjeje: ne themi se diçka ndodhi diku (something_happened), për shembull, artikulli u anulua ose ndodhi një rimbursim. Nëse dikush e dëgjon këto ngjarje, atëherë për 'artikulli u anulua' do të krijohet entiteti i rimbursimit, dhe 'ndodhi një rimbursim' do të regjistrohet diku në konfigurime.

Por zakonisht, kur projektoni ngjarje, nuk doni t'i shkruani ato kot — ju mbështeteni se dikush do t'i lexojë ato. Ka një tundim të lartë të shkruani diçka që nuk është something_happened (item_canceled, refund_refunded), por diçka si something_should_be_done. Për shembull, artikulli është gati për kthim.

Nga njëra anë, kjo tregon se si do të përdoret ngjarja. Nga ana tjetër, kjo duket shumë më pak si një emër normal ngjarjeje. Poshtë kësaj, është një hap i vogël deri te komanda do_something. Por nuk keni garanci se kjo ngjarje do të lexohet nga dikush; dhe nëse është lexuar, atëherë është lexuar me sukses; dhe nëse është lexuar me sukses, atëherë bëri diçka, dhe ajo diçka kaloi me sukses. Në momentin që ngjarja bëhet do_something, nevoja për përgjigje bëhet e domosdoshme, dhe kjo është një problem.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Në shkëmbimin asinkron në RabbitMQ, kur lexoni një mesazh dhe bëni një kërkesë http, keni një përgjigje - pavarësisht se mesazhi është pranuar. Kur shkruani në Kafka, ka një mesazh që e keni shkruar në Kafka, por nuk dini asgjë për mënyrën se si u procesua.

Prandaj, në rastin tonë, ishte e nevojshme të futnim një ngjarje përgjigjeje dhe të konfiguroni monitorimin për atë që, nëse ndodhin kaq shumë ngjarje, pas një kohe të caktuar duhet të arrijnë kaq shumë ngjarje përgjigjeje. Nëse kjo nuk ndodhi, duket se diçka shkoi keq. Për shembull, nëse dërguam ngjarjen «item_ready_to_refund», presim që të krijohet një rimbursim, klientit tia kthejmë paratë, dhe të na dalë ngjarja «money_refunded». Por kjo nuk është e sigurt, prandaj është e nevojshme të monitorohet.

Nuancat

Ka një problem të dukshëm: nëse lexoni me radhë nga një temë dhe keni një mesazh të keq, konsumatori bie, dhe më pas nuk mund të vazhdoni. Ju nevojitet të ndaloni të gjithë konsumatorët, të komitoni offset-in më tej, për të vazhduar leximin.

Ne e dimë që kjo ndodhi, e planifikuam, por megjithatë ndodhi. Këtu ndodhi sepse ngjarja ishte e vlefshme sipas events-bus, ngjarja ishte e vlefshme sipas validatorit të aplikacionit, por nuk ishte e vlefshme sipas PostgreSQL, sepse kemi një sistem MySQL me UNSIGNED INT, ndërsa në sistemin e ri kishte vetëm PostgreSQL me INT. Ai ka një madhësi pak më të vogël dhe Id nuk u fut. Symfony u ndal me një përjashtim. Ne, natyrisht, kapëm përjashtimin, sepse e kishim parashikuar atë dhe planifikonim të angazhonim këtë offset, por para kësaj donim të inkrementonim numrin e problemeve, pasi mesazhi dështoi gjatë përpunimit. Numrat në këtë projekt gjithashtu janë në bazë, kurse Symfony tashmë e kishte mbyllur komunikimin me bazën, dhe përjashtimi i dytë vrau gjithë procesin pa mundësi për të angazhuar offset.

Për një kohë shërbimi qëndroi – për fat të mirë, me Kafka kjo nuk është aq e frikshme, sepse mesazhet mbeten. Kur puna të rifillojë, do të jetë e mundur të merren ato. Kjo është komode.

Në Kafka ka mundësi që përmes tooling të vendosni një offset të caktuar. Por për ta bërë këtë, duhet të ndaloni të gjithë konsumatorët — në rastin tonë, të përgatisim një version të veçantë, në të cilin nuk do të ketë konsumatorë, redeployments. Atëherë në Kafka përmes tooling mund të zhvendosni offset-in dhe mesazhi do të kalojë.

Një tjetër nuancë — replication log vs rdkafka.so — është i lidhur me specifikat e projektit tonë. Ne përdorim PHP dhe në PHP, zakonisht të gjitha bibliotekat komunikojnë me Kafka përmes repository rdkafka.so, dhe më pas vjen ndonjëlloj mbështetje. Ndoshta, këto janë vështirësitë tona personale, por rezultoi se të riflitet një copë e lexuar më parë nuk është aq e lehtë. Në përgjithësi, kishim probleme me softuerin.

Duke u kthyer në veçoritë e punës me partitions, në dokumentacionin e drejtpërdrejtë shkruhet consumers >= topic partitions. Por e mësova këtë shumë më vonë se sa doja. Nëse dëshironi të shkalloni dhe të keni dy konsumatorë, ju nevojiten të paktën dy partitions. Pra, nëse keni pasur një partition, në të cilin janë grumbulluar 20 mijë mesazhe dhe krijoni një të ri, numri i mesazheve nuk do të rregullohet shpejt. Prandaj, për të pasur dy konsumatorë paralelë, duhet të merret me partitions.

Monitorimi

Mendoj se, nga monitorimi ynë, do të jetë më e qartë se cilat probleme ka qasja aktuale.

Për shembull, ne numërojmë sa produkte në bazë kanë ndryshuar statusin së fundmi, dhe në përputhje me këto ndryshime, ndodhin ngjarje, dhe e dërgojmë këtë numër në sistemin tonë të monitorimit. Më pas, nga Kafka marrim numrin e dytë, sa ngjarje janë regjistruar në të vërtetë. Është e qartë, diferenca mes këtyre dy numrave gjithmonë duhet të jetë zero.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Përveç kësaj, duhet të monitorojmë se si po shkon puna me prodhuesin, nëse events-bus ka pranuar mesazhet, dhe si po shkon puna me konsumatorin. Për shembull, në grafiket më poshtë, gjithçka është mirë me Refund Tool, por me BOB dukshëm ka disa probleme (pikat blu).

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Kisha përmendur tashmë vonesën e grupit të konsumatorëve. Në thelb, kjo është numri i mesazheve të papara. Në përgjithësi, konsumatorët tanë punojnë shpejt, prandaj vonesa zakonisht është 0, por ndonjëherë mund të ketë një pikë të shkurtër. Kafka e bën këtë nga kuti, por është e nevojshme të vendosim një interval.

Ka një projekt Burrow, që do t'ju japë më shumë informacion mbi Kafka. Ai thjesht jep statusin e grupit të konsumatorëve përmes API-së. Përveç OK dhe Failed, ka një warning, dhe do të jeni në gjendje të dini nëse konsumatorët tuaj nuk po përballohen me ritmin e prodhimit – nuk arrijnë të lexojnë atë që shkruhet. Sistemi është mjaft i mençur, është lehtë për t'u përdorur.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Këtu është si duket përgjigja në API. Këtu grupi bob-live-fifa, ndarja refund.update.v1, statusi OK, lag 0 – offseti i fundit është i tillë.

Eksperienca në zhvillimin e shërbimit Refund Tool me API asinkron në Kafka

Monitorimi updated_at SLA (ngrirë) unë tashmë e kam përmendur. Për shembull, produkti kaloi në statusin që është gati për kthim. Ne vendosim një Cron, që thotë se nëse brenda 5 minutave ky objekt nuk kalon në refund (ne i kthejmë paratë përmes sistemeve të pagesave shumë shpejt), atëherë diçka ka shkuar ndryshe, dhe ky është një rast i sigurt për suportin. Prandaj, thjesht marrim Cron-in që lexon këto gjëra dhe nëse ato janë më shumë se 0, dërgon një alarme.

Në përfundim, përdorimi i ngjarjeve është i përshtatshëm kur:

  • informacioni kërkohet nga disa sisteme;
  • rezultati i përpunimit nuk ka rëndësi;
  • ngjarjet janë pak ose ngjarjet janë të vogla.

Duket sikur artikulli ka një temë shumë specifike - API asinkron në Kafka, por në lidhje me të ka shumë të tjera që do të rekomandoja menjëherë.
Së pari, e ardhmja HighLoad++ nuk duhet të prisni deri në nëntor, versioni i Shën Petersburgut do të jetë në prill, dhe në qershor do të flasim për ngarkesat e rënda në Novosibirsk.
Së dyti, autori i raportit, Sergei Zaika, është pjesë e Komitetit Organizativ të konferencës sonë të re për menaxhimin e njohurive KnowledgeConf. Konferenca do të jetë një ditore, do të zhvillohet më 26 prill, por programi i saj është shumë i pasur.
Dhe në maj do të ketë PHP Russia dhe RIT++ (me DevOpsConf në përbërje) - atje mund të ofroni ende temën tuaj, të flisni për përvojën tuaj dhe të ankohemi për dhëmbjet tuaja.

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster