Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Wat kan 'n maatskappy so groot soos Lamoda, met 'n vaartbelynde proses en dosyne onderling gekoppelde dienste, 'n beduidende verandering in benadering maak? Motivering kan heeltemal anders wees: van wetgewende tot die begeerte om te eksperimenteer wat inherent is aan alle programmeerders.

Maar dit beteken nie dat u nie op bykomende voordele kan staatmaak nie. Wat presies kan jy wen as jy die gebeurtenisgedrewe API op Kafka implementeer, sal Sergey Zaika vertel (paarald). Daar sal ook seker wees oor opgestopte keëls en interessante ontdekkings – die eksperiment kan nie daarsonder nie.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Vrywaring: Hierdie artikel is gebaseer op materiaal van die ontmoeting wat Sergey in November 2018 by HighLoad++ gehou het. Lamoda se lewendige ervaring van die werk met Kafka het luisteraars gelok nie minder nie as ander verslae van die skedule. Dit lyk vir ons of dit 'n goeie voorbeeld is van die feit dat dit altyd moontlik en nodig is om eendersdenkende mense te vind, en die organiseerders van HighLoad ++ sal aanhou probeer om 'n atmosfeer te skep wat daartoe bydra.

Oor die proses

Lamoda is 'n groot e-handelsplatform wat sy eie kontaksentrum, afleweringsdiens (en baie vennote), 'n fotoateljee, 'n groot pakhuis het, en dit alles werk op sy eie sagteware. Daar is dosyne betaalmetodes, b2b-vennote wat sommige of al hierdie dienste kan gebruik en die nuutste inligting oor hul produkte wil weet. Boonop werk Lamoda in drie lande behalwe die Russiese Federasie, en alles is 'n bietjie anders daar. In totaal is daar waarskynlik meer as honderd maniere om 'n nuwe bestelling op te stel, wat op sy eie manier verwerk moet word. Dit alles werk met behulp van tientalle dienste wat op soms nie-vanselfsprekende maniere kommunikeer. Daar is ook 'n sentrale stelsel wie se hoofverantwoordelikheid ordestatusse is. Ons noem haar BOB, ek werk saam met haar.

Terugbetalingnutsding met gebeurtenisgedrewe API

Die woord gebeurtenis-gedrewe is nogal deurmekaar, 'n bietjie verder sal ons in meer besonderhede definieer wat hiermee bedoel word. Ek sal begin met die konteks waarin ons besluit het om die gebeurtenisgedrewe API-benadering in Kafka te probeer.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

In enige winkel, benewens bestellings waarvoor klante betaal, is daar tye wanneer die winkel die geld moet teruggee omdat die produk nie by die kliënt pas nie. Dit is 'n relatief kort proses: ons maak die inligting duidelik, indien nodig, en dra die geld oor.

Maar die opgawe het ingewikkelder geword weens veranderinge in wetgewing, en ons moes ’n aparte mikrodiens daarvoor implementeer.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Ons motivering:

  1. Wet FZ-54 - kortom, die wet vereis om aan die belastingkantoor verslag te doen oor elke geldelike transaksie, of dit nou 'n opgawe of kwitansie is, in 'n redelik kort SLA van 'n paar minute. Ons, as e-handel, voer nogal baie operasies uit. Tegnies beteken dit nuwe verantwoordelikheid (en dus 'n nuwe diens) en verbeterings in alle betrokke stelsels.
  2. BOB verdeel - 'n interne projek van die maatskappy om BOB van 'n groot aantal nie-kernverantwoordelikhede te ontslae te raak en die algehele kompleksiteit daarvan te verminder.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Hierdie diagram toon die belangrikste Lamoda-stelsels. Nou is die meeste van hulle meer konstellasie van 5-10 mikrodienste rondom 'n krimpende monoliet. Hulle groei stadig, maar ons probeer hulle kleiner maak, want die ontplooiing van 'n fragment wat in die middel gekies is, is skrikwekkend - jy kan dit nie laat val nie. Ons word gedwing om alle uitruilings (pyltjies) te bespreek en te belowe dat enige van hulle dalk nie beskikbaar is nie.

Daar is ook heelwat uitruilings in BOB: betaalstelsels, aflewering, kennisgewings, ens.

Tegnies is BOB:

  • ~150k reëls kode + ~100k reëls toetse;
  • php7.2 + Zend 1 & Symfony Components 3;
  • >100 API's & ~50 uitgaande integrasies;
  • 4 lande met hul eie besigheidslogika.

Om 'n BOB te ontplooi is duur en pynlik, die hoeveelheid kode en die take wat dit oplos, is sodanig dat niemand dit in sy geheel in hul kop kan sit nie. Oor die algemeen is daar baie redes om dit te vereenvoudig.

Terugkeer proses

Aanvanklik is twee stelsels by die proses betrokke: BOB en Betaling. Nou is daar nog twee:

  • Fiskalisasiediens, wat sal sorg vir fiskalisasieprobleme en kommunikasie met eksterne dienste.
  • Terugbetalingsinstrument, waarin nuwe ruilings eenvoudig uitgeneem word om nie die BOB op te blaas nie.

Nou lyk die proses so:

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

  1. BOB ontvang 'n versoek vir 'n terugbetaling.
  2. BOB praat oor hierdie Terugbetalingsinstrument.
  3. Die terugbetalingsinstrument sê vir betaling: "Terug die geld terug."
  4. Betaling gee die geld terug.
  5. Refund Tool en BOB sinchroniseer statusse tussen mekaar, want vir nou het albei van hulle dit nodig. Ons is nog nie gereed om heeltemal na die Terugbetalingsinstrument oor te skakel nie, aangesien BOB 'n UI het, verslae vir rekeningkunde, en in die algemeen baie data wat nie so maklik oorgedra kan word nie. Jy moet op twee stoele sit.
  6. Die versoek om fiskalisering vertrek.

Gevolglik het ons 'n sekere geleentheidsbus op Kafka gemaak - gebeurtenisbus, waarop alles begin het. Hoera, nou het ons 'n enkele punt van mislukking (sarkasme).

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Die voor- en nadele is redelik voor die hand liggend. Ons het 'n bus gemaak, wat beteken dat alle dienste nou daarvan afhang. Dit vereenvoudig die ontwerp, maar stel 'n enkele punt van mislukking in die stelsel in. Kafka sal val, die proses sal styg.

Wat is die gebeurtenisgedrewe API

’n Goeie antwoord op hierdie vraag is in Martin Fowler se verslag (GOTO 2017) "Die vele betekenisse van gebeurtenisgedrewe argitektuur".

Kortliks wat ons gedoen het:

  1. Het alle asynchrone uitruilings ingevul via berging van gebeurtenisse. In plaas daarvan om elke belangstellende verbruiker in te lig oor 'n statusverandering oor die netwerk, skryf ons 'n staatsveranderingsgeleentheid na 'n gesentraliseerde winkel, en verbruikers wat in 'n onderwerp belangstel, lees alles wat daarvandaan verskyn.
  2. Gebeurtenis (gebeurtenis) in hierdie geval is 'n kennisgewing (notifikasies) dat iets iewers verander het. Byvoorbeeld, die bestellingstatus het verander. 'n Verbruiker wat omgee vir sommige data wat die statusverandering vergesel en wat nie in die kennisgewing is nie, kan self hul status uitvind.
  3. Die maksimum opsie is volwaardige gebeurtenis verkryging, staatsoordrag, waarin die gebeurtenis al die inligting bevat wat nodig is vir verwerking: van waar en na watter status hulle oorgeskakel het, hoe presies die data verander het, ens. Die enigste vraag is die doeltreffendheid en die hoeveelheid inligting wat jy kan bekostig om te stoor.

As deel van die bekendstelling van die Terugbetalingsinstrument het ons die derde opsie gebruik. Dit vergemaklik gebeurtenishantering omdat geen gedetailleerde inligting herwin hoef te word nie, en dit elimineer die scenario waar elke nuwe gebeurtenis 'n vlaag van soektogversoeke van verbruikers veroorsaak.

Diensterugbetalingsinstrument afgelaai, so Kafka is meer 'n toets as 'n noodsaaklikheid. Ek dink nie dat as die terugbetalingsdiens 'n hoëladingsprojek sou word, die besigheid gelukkig sou wees nie.

Asynchrone uitruil AS IS

Vir asynchrone uitruilings gebruik die PHP-afdeling gewoonlik RabbitMQ. Ons het die data vir die versoek ingesamel, dit in die ry geplaas, en die verbruiker van dieselfde diens het dit getel en gestuur (of nie gestuur nie). Vir die API self gebruik Lamoda Swagger aktief. Ons ontwerp die API, beskryf dit in Swagger, genereer kliënt- en bedienerkode. Ons gebruik ook effens uitgebreide JSON RPC 2.0.

In sommige plekke word esb-busse gebruik, iemand leef op activeMQ, maar oor die algemeen, RabbitMQ - standaard.

Asinc-uitruil OM TE WEES

Wanneer 'n uitruil deur middel van gebeurtenisse-bus ontwerp word, kan 'n analogie opgespoor word. Ons beskryf op soortgelyke wyse die toekomstige data-uitruiling deur gebeurtenisstruktuurbeskrywings. Die yaml-formaat, ons moes self die kodegenerering doen, die kragopwekker skep DTO's volgens die spesifikasie en leer kliënte en bedieners om daarmee te werk. Generasie gaan in twee tale - golang en php. Dit hou die biblioteke konsekwent. Die kragopwekker is in golang geskryf, waarvoor dit die naam gogi gekry het.

Gebeurtenisverkryging op Kafka is 'n tipiese ding. Daar is 'n oplossing van die hoofonderneming weergawe van Kafka Confluent, daar is nakadi, 'n oplossing van ons "broers" in die domeingebied Zalando. Ons motivering om met vanielje Kafka te begin is om die oplossing vry te laat totdat ons finaal besluit of ons dit oral gaan gebruik, en onsself ook ruimte vir maneuver en verbeterings te laat: ons wil ondersteuning hê vir ons JSON RPC 2.0, kragopwekkers vir twee tale en kom ons kyk wat nog.

Dit is ironies dat selfs in so 'n gelukkige geval, wanneer daar 'n soortgelyke Zalando-onderneming is wat 'n soortgelyke oplossing gemaak het, ons dit nie effektief kan gebruik nie.

Argitektonies, by die begin, is die patroon soos volg: ons lees direk vanaf Kafka, maar ons skryf slegs deur gebeurtenisse-bus. Daar is baie klaargemaakte goed om in Kafka te lees: makelaars, balanseerders, en dit is min of meer gereed vir horisontale skaal, ek wou dit behou. Die rekord is dat ons deur een Gateway aka Events-bus wou draai, en hier is hoekom.

Gebeurtenisse-bus

Of die geleentheidsbus. Dit is net 'n staatlose http-poort wat verskeie belangrike rolle aanneem:

  • Produseer validering - maak seker dat die gebeure aan ons spesifikasie voldoen.
  • Bemeester stelsel deur gebeure, dit wil sê, dit is die belangrikste en enigste stelsel in die maatskappy wat die vraag beantwoord van watter gebeure met watter strukture as geldig beskou word. Validasie is net datatipes en opsommings vir rigiede inhoudspesifikasie.
  • hash funksie vir sharding - die struktuur van die Kafka-boodskap is sleutel-waarde en dit word bereken uit die hash van sleutel waar om dit te plaas.

Hoekom

Ons werk in 'n groot maatskappy met 'n vaartbelynde proses. Hoekom iets verander? Dit is 'n eksperimenten ons verwag om verskeie voordele te kry.

1:n+1-uitruilings (een tot baie)

Met Kafka is dit baie maklik om nuwe verbruikers aan die API te koppel.

Kom ons sê jy het 'n gids wat in verskeie stelsels gelyktydig (en in sommige nuwes) op datum gehou moet word. Voorheen het ons 'n bondel uitgevind wat die set-API geïmplementeer het, en die adres van die verbruikers is aan die meesterstelsel gerapporteer. Nou stuur die meesterstelsel opdaterings na die onderwerp, en almal wat belangstel lees. 'n Nuwe stelsel het verskyn - hulle het dit oor die onderwerp onderteken. Ja, ook 'n bundel, maar eenvoudiger.

In die geval van die terugbetaling-instrument, wat 'n stuk BOB is, is dit gerieflik vir ons om hulle deur Kafka gesinchroniseer te hou. Payment sê die geld is teruggegee: BOB, RT het hiervan uitgevind, hul status verander, Fiskaliseringsdiens het hiervan uitgevind en 'n tjek uitgeslaan.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Ons het planne om 'n enkele Kennisgewingsdiens te maak wat die kliënt in kennis stel van die nuus oor sy bestelling/opgawes. Nou is hierdie verantwoordelikheid oor stelsels versprei. Dit sal vir ons genoeg wees om die Kennisgewingsdiens te leer om relevante inligting van Kafka op te vang en daarop te reageer (en hierdie kennisgewings in ander stelsels uit te skakel). Geen nuwe direkte uitruilings sal vereis word nie.

Datagedrewe

Inligting tussen stelsels word deursigtig - maak nie saak hoe "bloedige onderneming" jy het nie en maak nie saak hoe plomp jou agterstand is nie. Lamoda het 'n Data Analytics-afdeling wat data van stelsels insamel en dit omskep in 'n herbruikbare vorm vir beide besigheids- en intelligente stelsels. Kafka laat jou toe om vinnig vir hulle baie data te gee en hierdie inligtingvloei op datum te hou.

Replikasie log

Boodskappe verdwyn nie nadat dit gelees is nie, soos in RabbitMQ. Wanneer die gebeurtenis genoeg inligting bevat om te verwerk, het ons 'n geskiedenis van die jongste veranderinge aan die voorwerp, en, indien verlang, die vermoë om hierdie veranderinge toe te pas.

Die stoortydperk van die replikasielogboek hang af van die intensiteit van skryf aan hierdie onderwerp, Kafka laat jou toe om buigsaam limiete vir bergingstyd en datavolume te stel. Vir intensiewe onderwerpe is dit belangrik dat alle verbruikers tyd het om die inligting te lees voordat dit verdwyn, selfs in die geval van 'n korttermyn-onoperabiliteit. Gewoonlik is dit moontlik om data te stoor vir eenhede van dae, wat heeltemal genoeg is vir 'n ondersteuning.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Verder, 'n bietjie hervertelling van die dokumentasie, vir diegene wat nie met Kafka vertroud is nie (die foto is ook uit die dokumentasie)

Daar is toue in AMQP: ons skryf boodskappe na die tou vir die verbruiker. As 'n reël verwerk een stelsel een tou met dieselfde besigheidslogika. As jy verskeie stelsels in kennis moet stel, kan jy die toepassing leer om na verskeie toue te skryf of 'n uitruiling opstel met 'n uitwaaimeganisme wat hulle kloon.

Kafka het 'n soortgelyke abstraksie onderwerp, waarin jy boodskappe skryf, maar hulle verdwyn nie na lees nie. By verstek, wanneer jy aan Kafka koppel, ontvang jy alle boodskappe, en jy het die opsie om te stoor waar jy opgehou het. Dit wil sê, jy lees opeenvolgend, jy kan nie die boodskap as gelees merk nie, maar die ID stoor, waaruit jy later kan voortgaan om te lees. Die id waarby jy gestop het, word offset (offset) genoem, en die meganisme word commit offset genoem.

Gevolglik kan verskillende logika geïmplementeer word. Ons het byvoorbeeld BOB in 4 gevalle vir verskillende lande - Lamoda is in Rusland, Kazakhstan, Oekraïne, Wit-Rusland. Aangesien hulle afsonderlik ontplooi word, het hulle effens hul eie konfigurasies en hul eie besigheidslogika. Ons dui in die boodskap aan na watter land dit verwys. Elke BOB-verbruiker in elke land lees met 'n ander groupId, en as die boodskap nie daarop van toepassing is nie, slaan hulle dit oor, m.a.w. pleeg onmiddellik offset +1. As dieselfde onderwerp deur ons Betaaldiens gelees word, doen dit dit met 'n aparte groep, en daarom sny verskuiwings nie.

Gebeurtenisvereistes:

  • Volledigheid van data. Ek wil graag hê die geleentheid moet genoeg data hê sodat dit verwerk kan word.

  • Integriteit. Ons delegeer aan Events-bus die kontrole dat die geleentheid konsekwent is en dat dit dit kan verwerk.
  • Die volgorde is belangrik. In die geval van 'n terugkeer word ons gedwing om met geskiedenis te werk. Met kennisgewings is die bestelling nie belangrik nie, as dit homogene kennisgewings is, sal die e-pos dieselfde wees, ongeag watter bestelling eerste aangekom het. In die geval van 'n terugkeer is daar 'n duidelike proses, as jy die bestelling verander, dan sal uitsonderings ontstaan, die terugbetaling sal nie geskep of verwerk word nie - ons sal in 'n ander status beland.
  • Konsekwentheid. Ons het 'n bewaarplek, en nou skep ons gebeurtenisse in plaas van 'n API. Ons het 'n manier nodig om vinnig en goedkoop inligting oor nuwe gebeurtenisse en veranderinge aan bestaandes na ons dienste te stuur. Dit word bereik deur 'n gemeenskaplike spesifikasie in 'n aparte git-bewaarplek en kode-opwekkers. Daarom word kliënte en bedieners in verskillende dienste met ons gekoördineer.

Kafka in Lamoda

Ons het drie Kafka-installasies:

  1. logs;
  2. R&D;
  3. gebeure-bus.

Vandag praat ons net oor die laaste punt. In gebeurtenisse-bus het ons nie baie groot installasies nie - 3 makelaars (bedieners) en slegs 27 onderwerpe. As 'n reël is een onderwerp een proses. Maar dit is 'n subtiele punt, en nou sal ons dit aanraak.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Hierbo is die rps grafiek. Die terugbetalingsproses is gemerk met 'n turkoois lyn (ja, die een op die x-as), en pienk is die inhoudopdateringsproses.

Die Lamoda-katalogus bevat miljoene produkte, en die data word heeltyd bygewerk. Sommige versamelings gaan uit die mode, nuwes word in hul plek vrygestel, nuwe modelle verskyn voortdurend in die katalogus. Ons probeer voorspel waarin ons kliënte môre sal belangstel, so ons koop voortdurend nuwe goed, neem foto's daarvan en werk die vertoonvenster op.

Pienk pieke is produkopdaterings, dit wil sê produkveranderinge. Dit kan gesien word dat die ouens foto's geneem het, foto's geneem het, en dan weer! - 'n pak gebeurtenisse gelaai.

Lamoda Events gebruik gevalle

Ons gebruik die gekonstrueerde argitektuur vir die volgende bewerkings:

  • Terugkeer status dop: oproep tot aksie en statusnasporing van alle betrokke stelsels. Betaling, statusse, fiskalisering, kennisgewings. Hier het ons die benadering getoets, gereedskap gemaak, al die foute versamel, die dokumentasie geskryf en vir ons kollegas vertel hoe om dit te gebruik.
  • Produkkaartopdatering: konfigurasie, metadata, eienskappe. Een stelsel lees (wat vertoon), en verskeie skryf.
  • E-pos, druk en sms: die bestelling is saamgestel, die bestelling het aangekom, die terugsending is aanvaar, ens., daar is baie van hulle.
  • Voorraad, pakhuishernuwing - kwantitatiewe opdatering van name, net nommers: kwitansie by die pakhuis, terugstuur. Dit is nodig dat alle stelsels wat verband hou met die bespreking van goedere met die mees onlangse data werk. Op die oomblik is die wasbakopdateringstelsel redelik kompleks, Kafka sal dit vereenvoudig.
  • Data-analise (R&D-afdeling), ML-instrumente, analise, statistiek. Ons wil hê dat inligting deursigtig moet wees – Kafka is baie geskik hiervoor.

Nou die meer interessante deel oor opgestopte stampe en interessante ontdekkings wat in ses maande gebeur het.

Ontwerpkwessies

Kom ons sê ons wil 'n nuwe ding doen – byvoorbeeld, dra die hele afleweringsproses na Kafka oor. Nou is 'n deel van die proses geïmplementeer in Order Processing in BOB. Agter die oordrag van die bestelling na die afleweringsdiens, skuif na die tussenpakhuis, ensovoorts, is daar 'n statusmodel. Daar is 'n hele monoliet, selfs twee, plus 'n klomp API's wat aan aflewering toegewy is. Hulle weet baie meer van aflewering.

Dit blyk soortgelyke gebiede te wees, maar die statusse verskil vir bestellingverwerking in BOB en vir die afleweringstelsel. Sommige koerierdienste stuur byvoorbeeld nie tussenstatusse nie, maar slegs die finale: "afgelewer" of "verlore". Ander, inteendeel, berig in groot detail oor die beweging van goedere. Elkeen het hul eie valideringsreëls: vir iemand is die e-pos geldig, wat beteken dit sal verwerk word; vir ander is dit nie geldig nie, maar die bestelling sal steeds verwerk word, want daar is 'n telefoon vir kommunikasie, en iemand sal sê so 'n bestelling sal glad nie verwerk word nie.

Data stroom

In die geval van Kafka ontstaan ​​die vraag oor die organisering van die vloei van data. Hierdie taak hou verband met die keuse van strategie op verskeie punte, kom ons gaan deur hulle almal.

In een onderwerp of in 'n ander?

Ons het 'n geleentheidspesifikasie. In BOB skryf ons dat so en so 'n bestelling afgelewer moet word, en dui aan: die bestelnommer, die samestelling daarvan, sommige SKU's en strepieskodes, ens. Wanneer die goedere by die pakhuis aankom, sal die aflewering statusse, tydstempels en alles wat nodig is, kan ontvang. Maar dan wil ons opdaterings oor hierdie data in BOB ontvang. Ons het 'n omgekeerde proses om data van die aflewering te verkry. Is dit dieselfde gebeurtenis? Of is dit 'n aparte uitruiling wat 'n aparte onderwerp verdien?

Heel waarskynlik sal hulle baie soortgelyk wees, en die versoeking om een ​​onderwerp te maak is nie onredelik nie, want 'n aparte onderwerp beteken afsonderlike verbruikers, aparte konfigurasies, 'n aparte generasie van dit alles. Maar nie 'n feit nie.

Nuwe veld of nuwe gebeurtenis?

Maar as jy dieselfde gebeure gebruik, dan ontstaan ​​'n ander probleem. Byvoorbeeld, nie alle afleweringstelsels kan 'n DTO genereer wat 'n BOB kan genereer nie. Ons stuur hulle ID, maar hulle stoor hulle nie, want hulle het dit nie nodig nie, en vanuit die oogpunt van die begin van die gebeurtenis-bus-proses, is hierdie veld vereis.

As ons 'n reël vir die gebeurtenisbus instel dat hierdie veld vereis word, dan word ons gedwing om bykomende valideringsreëls in die BOB of in die begingebeurtenishanteerder te stel. Bekragtiging begin deur die hele diens versprei - dit is nie baie gerieflik nie.

Nog 'n probleem is die versoeking van inkrementele ontwikkeling. Ons word vertel dat ons iets by die geleentheid moet voeg, en miskien, as ons mooi dink, moes dit 'n aparte geleentheid gewees het. Maar in ons skema is 'n aparte gebeurtenis 'n aparte onderwerp. 'n Aparte onderwerp is die hele proses wat ek hierbo beskryf het. Die ontwikkelaar is in die versoeking om bloot nog een veld by die JSON-skema te voeg en dit te herskep.

In die geval van terugbetalings het ons oor 'n halfjaar by die geleentheid aangekom. Ons het een meta-gebeurtenis genaamd terugbetaling-opdatering gehad, wat 'n tipe veld gehad het wat beskryf wat hierdie opdatering eintlik is. Hieruit het ons "pragtige" skakelaars gehad met valideerders wat gesê het hoe om hierdie gebeurtenis met hierdie tipe te valideer.

Gebeurtenis weergawe

Om boodskappe in Kafka te bekragtig, kan jy gebruik Avro, maar dit was nodig om dadelik daarop neer te lê en Confluent te gebruik. In ons geval moet ons versigtig wees met weergawes. Dit sal nie altyd moontlik wees om boodskappe van die replikasielog te herlees nie, want die model het "vertrek". Basies blyk dit om weergawes te bou sodat die model agteruit versoenbaar is: maak byvoorbeeld 'n veld tydelik opsioneel. As die verskille te sterk is, begin ons in 'n nuwe onderwerp skryf, en die kliënte word oorgeplant wanneer hulle die ou een klaar gelees het.

Partisies lees bestelling waarborg

Onderwerpe binne Kafka word in partisies verdeel. Dit is nie baie belangrik terwyl ons entiteite en uitruilings ontwerp nie, maar dit is belangrik wanneer ons besluit hoe om dit te verbruik en te skaal.

In die normale geval skryf jy een onderwerp aan Kafka. By verstek word een partisie gebruik, en alle boodskappe van hierdie onderwerp val daarin. En die verbruiker lees hierdie boodskappe onderskeidelik opeenvolgend. Kom ons sê nou, ons moet die stelsel uitbrei sodat boodskappe deur twee verskillende verbruikers gelees word. As jy byvoorbeeld SMS'e stuur, dan kan jy vir Kafka sê om 'n bykomende partisie te maak, en Kafka sal begin om boodskappe in twee dele te ontbind - die helfte daar, die helfte daar.

Hoe deel Kafka hulle? Elke boodskap het 'n liggaam (waarin ons JSON stoor) en 'n sleutel. Jy kan 'n hash-funksie aan hierdie sleutel heg, wat sal bepaal in watter partisie die boodskap sal val.

In ons geval met terugbetalings is dit belangrik, as ons twee partisies neem, dan is daar 'n kans dat die parallelle verbruiker die tweede gebeurtenis voor die eerste sal verwerk en daar sal moeilikheid wees. Die hash-funksie verseker dat boodskappe met dieselfde sleutel in dieselfde partisie beland.

Gebeurtenisse vs opdragte

Dit is nog 'n probleem wat ons teëgekom het. 'n Gebeurtenis is 'n soort gebeurtenis: ons sê dat iets iewers gebeur het (iets_het gebeur), byvoorbeeld, 'n item is gekanselleer of 'n terugbetaling het plaasgevind. As iemand na hierdie gebeure luister, sal die terugbetalingsentiteit deur "item gekanselleer" geskep word, en "terugbetaling het gebeur" sal iewers in die opstellings aangeteken word.

Maar gewoonlik, wanneer jy geleenthede ontwerp, wil jy dit nie verniet skryf nie – jy wed dat iemand dit sal lees. Die versoeking is groot om nie iets_wat gebeur het nie (item_gekanselleer, terugbetaal_terugbetaal) te skryf, maar iets_moet_gedoen word. Die item is byvoorbeeld gereed om teruggestuur te word.

Aan die een kant dui dit op hoe die geleentheid gebruik gaan word. Aan die ander kant is dit baie minder soos 'n gewone gebeurtenisnaam. Boonop is dit van hier af nie ver van die doen_iets-opdrag nie. Maar jy het geen waarborg dat iemand hierdie gebeurtenis lees nie; en as dit gelees is, lees dan suksesvol; en as hy suksesvol gelees het, dan het hy iets gedoen, en hierdie iets was suksesvol. Die oomblik wat 'n gebeurtenis doen_iets word, word terugvoer nodig, en dit is die probleem.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

In 'n asynchrone uitruil in RabbitMQ, wanneer jy 'n boodskap lees, na http gaan, het jy 'n antwoord - ten minste dat die boodskap aanvaar is. Wanneer jy aan Kafka skryf, is daar 'n boodskap wat jy aan Kafka geskryf het, maar jy weet niks van hoe dit verwerk is nie.

Daarom moes ons in ons geval 'n reaksiegebeurtenis instel en monitering instel sodat as soveel gebeurtenisse uitgevlieg het, na so en so 'n tyd, dieselfde aantal reaksiegebeurtenisse moes opdaag. As dit nie gebeur nie, lyk dit of iets verkeerd geloop het. Byvoorbeeld, as ons die "item_ready_to_refund"-geleentheid gestuur het, verwag ons dat die terugbetaling geskep sal word, die kliënt sal die geld terug ontvang, en die "money_refunded"-geleentheid sal na ons toe vlieg. Maar dit is nie akkuraat nie, so monitering is nodig.

nuanses

Daar is 'n redelik duidelike probleem: as jy konsekwent uit 'n onderwerp lees, en jy het 'n soort slegte boodskap, val die verbruiker ineen, en jy sal nie verder gaan nie. Jy benodig stop alle verbruikers, pleeg offset verder om verder te lees.

Ons het daarvan geweet, ons wed daarop, en tog het dit in elk geval gebeur. En dit het gebeur omdat die gebeurtenis geldig was vanuit die oogpunt van gebeurtenisse-bus, die gebeurtenis was geldig vanuit die oogpunt van die aansoekvalideerder, maar dit was nie geldig vanuit die oogpunt van PostgreSQL nie, want in ons MySQL-stelsel met ONGETEKENDE INT, en in die nuutgeskrewe stelsel was PostgreSQL net met INT. Hy het 'n effens kleiner grootte, en Id het nie gepas nie. Symfony is met 'n uitsondering dood. Natuurlik het ons die uitsondering gevang, want ons was daarop gelê en gaan hierdie verrekening pleeg, maar voor dit wou ons die probleemteller verhoog, aangesien die boodskap onsuksesvol verwerk is. Die tellers in hierdie projek is ook in die basis, en Symfony het reeds kommunikasie met die basis gesluit, en die tweede uitsondering het die hele proses doodgemaak sonder 'n kans om verrekening te pleeg.

Vir 'n rukkie het die diens gelê - gelukkig is dit met Kafka nie so skrikwekkend nie, want die boodskappe bly. Wanneer die werk herstel is, kan hulle gelees word. Dis gemaklik.

Kafka het die vermoë om 'n arbitrêre offset deur gereedskap te stel. Maar om dit te doen, moet jy alle verbruikers stop - in ons geval, berei 'n aparte vrystelling voor waarin daar geen verbruikers, herontplooiings sal wees nie. Dan kan Kafka die offset deur gereedskap verskuif, en die boodskap sal verbygaan.

Nog 'n nuanse - replikasie log vs rdkafka.so - wat verband hou met die besonderhede van ons projek. Ons het PHP, en in PHP, as 'n reël, kommunikeer alle biblioteke met Kafka deur die rdkafka.so-bewaarplek, en dan is daar 'n soort omhulsel. Miskien is dit ons persoonlike probleme, maar dit het geblyk dat dit nie so maklik is om net weer 'n stukkie te lees van wat reeds gelees is nie. Oor die algemeen was daar sagtewareprobleme.

Om terug te keer na die eienaardighede van die werk met partisies, direk in die dokumentasie is dit geskryf verbruikers >= onderwerp partisies. Maar ek het baie later daarvan uitgevind as wat ek sou wou hê. As jy wil skaal en twee verbruikers het, benodig jy ten minste twee partisies. Dit wil sê, as jy een partisie gehad het waarin 20 duisend boodskappe opgehoop het, en jy het 'n nuwe een gemaak, sal die aantal boodskappe nie ewe gou gelyk word nie. Daarom, om twee parallelle verbruikers te hê, moet jy met partisies handel.

Monitering

Ek dink die manier waarop ons dit monitor, sal dit nog duideliker maak watter probleme daar in die bestaande benadering is.

Ons tel byvoorbeeld hoeveel produkte in die databasis onlangs hul status verander het, en gevolglik moes gebeure op hierdie veranderinge gebeur het, en ons stuur hierdie nommer na ons moniteringstelsel. Dan van Kafka kry ons die tweede nommer, hoeveel gebeurtenisse werklik geregistreer is. Dit is duidelik dat die verskil tussen hierdie twee getalle altyd nul moet wees.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Daarbenewens moet jy monitor hoe die vervaardiger vaar, of gebeurtenisse-bus boodskappe ontvang het, en hoe dit met die verbruiker gaan. Byvoorbeeld, in die grafieke hieronder vaar die Terugbetalingsinstrument goed, maar BOB het duidelik 'n paar probleme (blou pieke).

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Ek het reeds verbruikersgroepvertraging genoem. Rofweg gesproke is dit die aantal ongeleesde boodskappe. Oor die algemeen werk ons ​​verbruikers vinnig, so die vertraging is gewoonlik 0, maar soms kan daar 'n korttermynpiek wees. Kafka kan dit buite die boks doen, maar jy moet 'n interval instel.

Daar is 'n projek Burrow, wat jou meer inligting oor Kafka sal gee. Dit gee bloot die status via die verbruikersgroep-API, soos hierdie groep het. Benewens OK en Failed, is daar 'n waarskuwing daar, en jy kan uitvind dat jou verbruikers nie kan tred hou met die pas van produksie nie - hulle het nie tyd om te proeflees wat geskryf word nie. Die stelsel is redelik slim en maklik om te gebruik.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Dit is hoe die API-reaksie lyk. Hier is die bob-live-fifa-groep, partisie refund.update.v1, OK status, lag 0 die laaste finale verrekening so en so.

Ervaring in die ontwikkeling van die Refund Tool-diens met asynchrone API op Kafka

Monitering opgedateer_by SLA (vas) Ek het reeds genoem. Die item het byvoorbeeld verskuif na die status dat dit gereed is vir terugbesorging. Ons het Cron gestel, wat sê dat as hierdie voorwerp nie binne 5 minute terugbesorg is om terug te betaal nie (ons gee baie vinnig geld terug deur betalingstelsels), dan het iets beslis verkeerd geloop, en dit is beslis 'n saak vir ondersteuning. Daarom neem ons eenvoudig Cron, wat sulke dinge lees, en as hulle groter as 0 is, stuur dit 'n waarskuwing.

Om op te som, die gebruik van gebeure is nuttig wanneer:

  • inligting word deur verskeie stelsels benodig;
  • die resultaat van verwerking is nie belangrik nie;
  • min of geen gebeure nie.

Dit wil voorkom asof die artikel 'n baie spesifieke onderwerp het - 'n asynchrone API op Kafka, maar in verband daarmee wil ek dadelik baie dinge aanbeveel.
Eerste, volgende Hoëlaai++ maar jy moet wag tot November, in April sal daar sy St. Petersburg-weergawe wees, en in Junie sal ons praat oor hoë vragte in Novosibirsk.
Tweedens, die skrywer van die verslag Sergey Zaika is 'n lid van die Programkomitee van ons nuwe konferensie oor kennisbestuur KnowledgeConf. Die konferensie is eendag, sal op 26 April gehou word, maar die program is baie ryk.
En in Mei sal dit wees PHP Rusland и RIT++ (met DevOpsConf as deel) - daar kan jy ook jou onderwerp aanbied, oor jou ervaring praat en kla oor jou opgestopte stampe.

Bron: will.com

Voeg 'n opmerking