Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Wat zou zo’n groot bedrijf als Lamoda, met een gestroomlijnd proces en tientallen onderling verbonden diensten, kunnen dwingen zijn aanpak aanzienlijk te veranderen? Motivatie kan heel anders zijn: van wetgevend tot de wens om te experimenteren die inherent is aan alle programmeurs.

Maar dit betekent niet dat u niet kunt rekenen op extra voordelen. Sergey Zaika vertelt je wat je precies kunt winnen als je de event-driven API op Kafka implementeert (weinig). Er zal zeker ook gesproken worden over grote schoten en interessante ontdekkingen - het experiment kan niet zonder hen.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Disclaimer: dit artikel is gebaseerd op materiaal van een bijeenkomst die Sergey in november 2018 hield op HighLoad++. Lamoda's live-ervaring met het werken met Kafka trok niet minder luisteraars dan andere reportages op het programma. Wij vinden dit een mooi voorbeeld van het feit dat je altijd gelijkgestemden kunt en moet vinden, en de organisatoren van HighLoad++ zullen blijven proberen een sfeer te creëren die daarvoor bevorderlijk is.

Over het proces

Lamoda is een groot e-commerceplatform met een eigen contactcenter, bezorgservice (en veel affiliates), een fotostudio, een enorm magazijn en dit alles draait op eigen software. Er zijn tientallen betaalmethoden, b2b-partners die sommige of al deze diensten kunnen gebruiken en actuele informatie over hun producten willen weten. Daarnaast is Lamoda naast de Russische Federatie actief in drie landen en daar is alles net even anders. In totaal zijn er waarschijnlijk meer dan honderd manieren om een ​​nieuwe bestelling te configureren, die op zijn eigen manier moet worden verwerkt. Dit alles werkt met behulp van tientallen diensten die soms op niet voor de hand liggende manieren communiceren. Er is ook een centraal systeem dat voornamelijk verantwoordelijk is voor de orderstatus. We noemen haar BOB, ik werk met haar.

Terugbetalingstool met gebeurtenisgestuurde API

Het woord ‘event-driven’ is nogal afgezaagd; verderop zullen we nader definiëren wat hiermee wordt bedoeld. Ik zal beginnen met de context waarin we besloten om de gebeurtenisgestuurde API-aanpak in Kafka uit te proberen.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

In elke winkel zijn er, naast bestellingen waarvoor klanten betalen, momenten waarop de winkel geld moet retourneren omdat het product niet bij de klant past. Dit is een relatief kort proces: wij verduidelijken de informatie, indien nodig, en maken het geld over.

Maar door veranderingen in de wetgeving werd het rendement ingewikkelder en moesten we er een aparte microservice voor implementeren.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Onze motivatie:

  1. Wet FZ-54 - Kortom, de wet vereist dat elke geldtransactie, of het nu een aangifte of een ontvangstbewijs is, binnen een vrij korte SLA van enkele minuten aan de belastingdienst wordt gerapporteerd. Als e-commercebedrijf voeren wij heel wat handelingen uit. Technisch gezien betekent dit nieuwe verantwoordelijkheid (en dus een nieuwe service) en verbeteringen in alle betrokken systemen.
  2. BOB-splitsing is een intern project van het bedrijf om BOB te ontlasten van een groot aantal niet-kernverantwoordelijkheden en de algehele complexiteit ervan te verminderen.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Dit diagram toont de belangrijkste Lamoda-systemen. Nu zijn de meeste van hen meer een constellatie van 5-10 microservices rond een krimpende monoliet. Ze groeien langzaam, maar we proberen ze kleiner te maken, omdat het eng is om het in het midden geselecteerde fragment in te zetten - we kunnen niet toestaan ​​dat het valt. We zijn genoodzaakt om alle uitwisselingen (pijlen) te reserveren en er rekening mee te houden dat een van deze mogelijk niet beschikbaar blijkt te zijn.

BOB beschikt ook over heel wat uitwisselingen: betaalsystemen, bezorgsystemen, notificatiesystemen, enz.

Technisch gezien is BOB:

  • ~150 regels code + ~100 regels tests;
  • php7.2 + Zend 1 & Symfony-componenten 3;
  • >100 API's en ~50 uitgaande integraties;
  • 4 landen met hun eigen bedrijfslogica.

Het implementeren van BOB is duur en pijnlijk, de hoeveelheid code en problemen die het oplost is zodanig dat niemand het allemaal in zijn hoofd kan stoppen. Over het algemeen zijn er veel redenen om het te vereenvoudigen.

Retourproces

In eerste instantie zijn er twee systemen bij het proces betrokken: BOB en Betaling. Nu verschijnen er nog twee:

  • Fiscalisatiedienst, die problemen met de fiscalisering en de communicatie met externe diensten zal oplossen.
  • Refund Tool, die eenvoudigweg nieuwe uitwisselingen bevat om de BOB niet op te blazen.

Het proces ziet er nu als volgt uit:

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

  1. BOB ontvangt een verzoek tot terugbetaling.
  2. BOB vertelt over deze Terugbetalingstool.
  3. De Terugbetalingstool vertelt Betaling: "Retourneer het geld."
  4. Betaling geeft het geld terug.
  5. Refund Tool en BOB synchroniseren statussen met elkaar, want voorlopig hebben ze het allebei nodig. We zijn nog niet klaar om volledig over te stappen op de Refund Tool, aangezien BOB een gebruikersinterface, rapporten voor de boekhouding en over het algemeen veel gegevens heeft die niet zo gemakkelijk kunnen worden overgedragen. Je moet op twee stoelen zitten.
  6. Het verzoek om fiscalisering verdwijnt.

Als resultaat hebben we op Kafka een soort evenementenbus gemaakt - evenementenbus, waarop alles begon. Hoera, nu hebben we één enkel punt van mislukking (sarcasme).

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

De voor- en nadelen zijn vrij duidelijk. We hebben een bus gemaakt, wat betekent dat nu alle diensten ervan afhankelijk zijn. Dit vereenvoudigt het ontwerp, maar introduceert een enkel storingspunt in het systeem. Kafka zal crashen, het proces zal stoppen.

Wat is een gebeurtenisgestuurde API

Een goed antwoord op deze vraag staat in het rapport van Martin Fowler (GOTO 2017) "De vele betekenissen van gebeurtenisgestuurde architectuur".

In het kort wat wij deden:

  1. Rond alle asynchrone uitwisselingen af ​​via opslag van evenementen. In plaats van elke geïnteresseerde consument te informeren over een statuswijziging via het netwerk, schrijven we een evenement over een statuswijziging naar een gecentraliseerde opslag, en consumenten die geïnteresseerd zijn in het onderwerp lezen alles wat daaruit blijkt.
  2. De gebeurtenis is in dit geval een melding (meldingen) dat er ergens iets is veranderd. De bestelstatus is bijvoorbeeld gewijzigd. Een consument die geïnteresseerd is in bepaalde gegevens bij de statuswijziging die niet in de melding zijn opgenomen, kan zelf de status ervan achterhalen.
  3. De maximale optie is volwaardige evenementensourcing, staatsoverdracht, in welk geval alle informatie bevat die nodig is voor de verwerking: waar het vandaan komt en welke status het heeft gekregen, hoe de gegevens precies zijn gewijzigd, enz. De enige vraag is de haalbaarheid en de hoeveelheid informatie die u zich kunt veroorloven om op te slaan.

Als onderdeel van de lancering van de Refund Tool hebben we de derde optie gebruikt. Deze vereenvoudigde gebeurtenisverwerking, omdat het niet nodig was om gedetailleerde informatie te extraheren, en elimineerde het scenario waarin elke nieuwe gebeurtenis een reeks verhelderende verzoeken van consumenten genereert.

Restitutie gereedschapsservice niet geladenBij Kafka is er dus meer sprake van een voorproefje van de pen dan van noodzaak. Ik denk niet dat het bedrijfsleven blij zou zijn als de terugbetalingsservice een project met veel werk zou worden.

Asynchrone uitwisseling AS IS

Voor asynchrone uitwisselingen gebruikt de PHP-afdeling meestal RabbitMQ. We hebben de gegevens voor het verzoek verzameld, in een wachtrij geplaatst en de consument van dezelfde dienst heeft het gelezen en verzonden (of niet verzonden). Voor de API zelf maakt Lamoda actief gebruik van Swagger. We ontwerpen een API, beschrijven deze in Swagger en genereren client- en servercode. We gebruiken ook een iets verbeterde JSON RPC 2.0.

Op sommige plaatsen worden ESB-bussen gebruikt, sommige live op activeMQ, maar over het algemeen RabbitMQ - standaard.

Asynchrone uitwisseling TO BE

Bij het ontwerpen van uitwisseling via evenementenbus kan een analogie worden ontdekt. Op dezelfde manier beschrijven we toekomstige gegevensuitwisseling via beschrijvingen van de gebeurtenisstructuur. Door het yaml-formaat moesten we zelf de code genereren, de generator creëert DTO's volgens de specificatie en leert clients en servers ermee te werken. Generatie gaat in twee talen - golang en php. Dit helpt bibliotheken consistent te houden. De generator is geschreven in golang en heeft daarom de naam gogi gekregen.

Eventsourcing op Kafka is typisch iets. Er is een oplossing van de hoofdversie van Kafka Confluent, die is er nakadi, een oplossing van onze domeinbroeders Zalando. Ons motivatie om te beginnen met vanille Kafka - dit betekent dat we de oplossing vrij moeten laten totdat we uiteindelijk beslissen of we deze overal zullen gebruiken, en dat we ook ruimte moeten laten voor manoeuvres en verbeteringen: we willen steun voor onze JSON RPC 2.0, generatoren voor twee talen en laten we eens kijken wat nog meer.

Het is ironisch dat zelfs in zo'n gelukkig geval, als er een ongeveer vergelijkbaar bedrijf is, Zalando, dat een ongeveer vergelijkbare oplossing heeft bedacht, we daar geen effectief gebruik van kunnen maken.

Het architecturale patroon bij de lancering is als volgt: we lezen rechtstreeks uit Kafka, maar schrijven alleen via de evenementenbus. Er staat veel klaar om te lezen in Kafka: makelaars, balancers, en het is min of meer klaar voor horizontale schaalvergroting, dit wilde ik behouden. We wilden de opname voltooien via één Gateway oftewel Events-bus, en dit is waarom.

Evenementen-bus

Of een evenementenbus. Dit is eenvoudigweg een staatloze http-gateway, die verschillende belangrijke rollen vervult:

  • Validatie produceren — we controleren of de evenementen aan onze specificaties voldoen.
  • GebeurtenismastersysteemDat wil zeggen, dit is het belangrijkste en enige systeem in het bedrijf dat de vraag beantwoordt welke gebeurtenissen met welke structuren als geldig worden beschouwd. Validatie omvat eenvoudigweg gegevenstypen en opsommingen om de inhoud strikt te specificeren.
  • Hash-functie voor sharding: de Kafka-berichtstructuur is sleutelwaarde en met behulp van de hash van sleutel wordt berekend waar deze moet worden geplaatst.

Waarom

Wij werken in een groot bedrijf met een gestroomlijnd proces. Waarom iets veranderen? Dit is een experiment, en we verwachten verschillende voordelen te kunnen oogsten.

1:n+1 uitwisselingen (één tot veel)

Kafka maakt het heel eenvoudig om nieuwe consumenten aan de API te koppelen.

Stel dat u een directory heeft die u op meerdere systemen tegelijk (en in sommige nieuwe) up-to-date moet houden. Eerder hebben we een bundel uitgevonden die set-API implementeerde, en het mastersysteem werd op de hoogte gebracht van consumentenadressen. Nu stuurt het hoofdsysteem updates over het onderwerp en iedereen die geïnteresseerd is, leest het. Er is een nieuw systeem verschenen - we hebben het aangemeld voor het onderwerp. Ja, ook bundelen, maar dan eenvoudiger.

In het geval van de terugbetalingstool, wat een onderdeel van BOB is, is het handig voor ons om ze via Kafka gesynchroniseerd te houden. Betaling zegt dat het geld is teruggegeven: BOB, RT kwam erachter, veranderde hun status, de Fiscalisatiedienst kwam erachter en gaf een cheque uit.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

We hebben plannen om een ​​uniforme meldingsservice te creëren die de klant op de hoogte stelt van nieuws over zijn bestelling/retourzendingen. Nu is deze verantwoordelijkheid verspreid over de systemen. Het zal voor ons voldoende zijn om de Meldingsdienst te leren relevante informatie van Kafka op te vangen en erop te reageren (en deze meldingen in andere systemen uit te schakelen). Er zijn geen nieuwe directe uitwisselingen nodig.

Datagestuurd

Informatie tussen systemen wordt transparant, ongeacht wat voor “verdomde onderneming” u heeft en hoe groot uw achterstand ook is. Lamoda beschikt over een afdeling Data Analytics die data uit systemen verzamelt en in een herbruikbare vorm omzet, zowel voor het bedrijfsleven als voor intelligente systemen. Met Kafka kun je ze snel veel gegevens geven en die informatiestroom actueel houden.

Replicatielogboek

Berichten verdwijnen niet nadat ze zijn gelezen, zoals in RabbitMQ. Wanneer een gebeurtenis voldoende informatie bevat voor verwerking, hebben we een geschiedenis van recente wijzigingen aan het object en, indien gewenst, de mogelijkheid om deze wijzigingen toe te passen.

De opslagperiode van het replicatielogboek hangt af van de intensiteit van het schrijven naar dit onderwerp; met Kafka kunt u flexibel limieten instellen voor de opslagtijd en het gegevensvolume. Bij intensieve onderwerpen is het belangrijk dat alle consumenten de tijd hebben om de informatie te lezen voordat deze verdwijnt, zelfs als deze op korte termijn niet meer werkt. Meestal is het mogelijk om gegevens voor op te slaan eenheden van dagen, wat voldoende is voor ondersteuning.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Vervolgens een kleine hervertelling van de documentatie, voor degenen die niet bekend zijn met Kafka (de foto komt ook uit de documentatie)

AMQP heeft wachtrijen: we schrijven berichten naar een wachtrij voor de consument. Normaal gesproken wordt één wachtrij verwerkt door één systeem met dezelfde bedrijfslogica. Als u meerdere systemen moet waarschuwen, kunt u de applicatie leren naar verschillende wachtrijen te schrijven of de uitwisseling configureren met het fanout-mechanisme, dat ze zelf kloont.

Kafka heeft een soortgelijke abstractie onderwerp, waarin je berichten schrijft, maar deze verdwijnen niet na het lezen. Wanneer u verbinding maakt met Kafka, ontvangt u standaard alle berichten en heeft u de mogelijkheid om op te slaan waar u was gebleven. Dat wil zeggen, u leest het bericht opeenvolgend, u mag het bericht niet als gelezen markeren, maar het id opslaan van waaruit u vervolgens verder kunt lezen. De ID waarop u zich heeft gevestigd, wordt offset genoemd, en het mechanisme is commit-offset.

Dienovereenkomstig kan verschillende logica worden geïmplementeerd. We hebben bijvoorbeeld BOB in 4 gevallen voor verschillende landen: Lamoda bevindt zich in Rusland, Kazachstan, Oekraïne, Wit-Rusland. Omdat ze afzonderlijk worden geïmplementeerd, hebben ze enigszins verschillende configuraties en hun eigen bedrijfslogica. Wij geven in het bericht aan om welk land het gaat. Elke BOB-consument in elk land leest met een andere groupId, en als het bericht niet op hen van toepassing is, slaan ze het over, d.w.z. voert onmiddellijk een offset +1 uit. Als hetzelfde onderwerp door onze Betaalservice wordt gelezen, gebeurt dit met een aparte groep en kruisen de compensaties elkaar dus niet.

Eisen aan evenementen:

  • Volledigheid van gegevens. Ik wil graag dat de gebeurtenis over voldoende gegevens beschikt zodat deze kan worden verwerkt.

  • Integriteit We delegeren aan Events-bus de verificatie dat de gebeurtenis consistent is en deze kan verwerken.
  • De volgorde is belangrijk. In het geval van een terugkeer zijn we gedwongen met de geschiedenis te werken. Bij meldingen is de bestelling niet belangrijk. Als het homogene meldingen zijn, zal de e-mail hetzelfde zijn, ongeacht welke bestelling als eerste binnenkomt. In het geval van een terugbetaling is er een duidelijk proces; als we de bestelling wijzigen, zullen er uitzonderingen ontstaan, de terugbetaling wordt niet aangemaakt of verwerkt - we komen in een andere status terecht.
  • Samenhang. We hebben een winkel en nu maken we evenementen in plaats van een API. We hebben een manier nodig om snel en goedkoop informatie over nieuwe evenementen en wijzigingen in bestaande evenementen naar onze diensten te verzenden. Dit wordt bereikt door een gemeenschappelijke specificatie in een aparte git-repository en codegeneratoren. Daarom worden clients en servers in verschillende diensten op elkaar afgestemd.

Kafka in Lamoda

We beschikken over drie Kafka-installaties:

  1. Logboeken;
  2. O&O;
  3. Evenementen-bus.

Vandaag hebben we het alleen over het laatste punt. Bij events-bus hebben we geen erg grote installaties - 3 makelaars (servers) en slechts 27 topics. In de regel is één onderwerp één proces. Maar dit is een subtiel punt, en we zullen er nu op ingaan.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Hierboven ziet u de rps-grafiek. Het terugbetalingsproces is gemarkeerd met een turquoise lijn (ja, die op de X-as) en de roze lijn is het proces voor het bijwerken van de inhoud.

De Lamoda-catalogus bevat miljoenen producten en de gegevens worden voortdurend bijgewerkt. Sommige collecties raken uit de mode, er worden nieuwe uitgebracht om ze te vervangen en er verschijnen voortdurend nieuwe modellen in de catalogus. We proberen te voorspellen wat morgen interessant zal zijn voor onze klanten, dus we kopen voortdurend nieuwe dingen, fotograferen deze en vernieuwen de vitrine.

Roze pieken zijn productupdates, dat wil zeggen wijzigingen in producten. Het is te zien dat de jongens foto's maakten, foto's maakten en dan nog een keer! - een pakket met evenementen geladen.

Gebruiksscenario's van Lamoda Events

We gebruiken de geconstrueerde architectuur voor de volgende bewerkingen:

  • Volgen van de retourstatus: call-to-action en statustracking van alle betrokken systemen. Betaling, statussen, taxatie, notificaties. Hier hebben we de aanpak getest, tools gemaakt, alle bugs verzameld, documentatie geschreven en onze collega's verteld hoe ze deze moesten gebruiken.
  • Productkaarten bijwerken: configuratie, metadata, kenmerken. Eén systeem leest (wat wordt weergegeven) en verschillende schrijven.
  • E-mail, push en sms: de bestelling is opgehaald, de bestelling is aangekomen, de retourzending is geaccepteerd enz., het zijn er veel.
  • Voorraad, magazijnvernieuwing — kwantitatieve update van artikelen, alleen cijfers: aankomst in het magazijn, retour. Het is noodzakelijk dat alle systemen die verband houden met het reserveren van goederen werken met de meest actuele gegevens. Momenteel is het voorraadupdatesysteem behoorlijk complex; Kafka zal het vereenvoudigen.
  • Data-analyse (R&D-afdeling), ML-tools, analyses, statistieken. Wij willen dat informatie transparant is, daar is Kafka zeer geschikt voor.

Nu het interessantere deel over de grote hobbels en interessante ontdekkingen die de afgelopen zes maanden hebben plaatsgevonden.

Ontwerpproblemen

Laten we zeggen dat we iets nieuws willen doen, bijvoorbeeld het hele leveringsproces overdragen aan Kafka. Nu is een deel van het proces geïmplementeerd in Orderverwerking in BOB. Er zit een statusmodel achter de overdracht van een bestelling aan de bezorgservice, de verplaatsing naar een tussenmagazijn, enzovoort. Er is een hele monoliet, zelfs twee, plus een aantal API’s die speciaal zijn bedoeld voor bezorging. Zij weten veel meer over bezorgen.

Dit lijken vergelijkbare gebieden te zijn, maar de orderverwerking in BOB en het verzendsysteem hebben verschillende statussen. Sommige koeriersdiensten sturen bijvoorbeeld geen tussenstatussen, maar alleen de definitieve statussen: “bezorgd” of “verloren”. Anderen daarentegen doen zeer gedetailleerd verslag van het goederenverkeer. Iedereen heeft zijn eigen validatieregels: voor sommigen is de e-mail geldig, wat betekent dat deze wordt verwerkt; voor anderen is het niet geldig, maar de bestelling wordt toch verwerkt omdat er een telefoonnummer is voor contact, en iemand zal zeggen dat zo'n bestelling helemaal niet wordt verwerkt.

Data stroom

In het geval van Kafka rijst de kwestie van het organiseren van de datastroom. Deze taak omvat het kiezen van een strategie op basis van verschillende punten; laten we ze allemaal doornemen.

In één onderwerp of in verschillende?

We hebben een evenementenspecificatie. In BOB schrijven we dat die en die bestelling geleverd moet worden, en geven aan: het bestelnummer, de samenstelling ervan, enkele SKU's en barcodes, etc. Wanneer de goederen in het magazijn aankomen, kan de levering statussen, tijdstempels en alles wat nodig is ontvangen. Maar dan willen we updates over deze gegevens ontvangen in BOB. Wij hanteren een omgekeerde procedure voor het ontvangen van gegevens vanaf de bezorging. Is dit dezelfde gebeurtenis? Of is dit een aparte uitwisseling die een eigen onderwerp verdient?

Hoogstwaarschijnlijk zullen ze erg op elkaar lijken, en de verleiding om één onderwerp te maken is niet ongegrond, omdat een apart onderwerp afzonderlijke consumenten betekent, afzonderlijke configuraties, een afzonderlijke generatie van dit alles. Maar geen feit.

Nieuw veld of nieuw evenement?

Maar als je dezelfde gebeurtenissen gebruikt, ontstaat er een ander probleem. Niet alle leveringssystemen kunnen bijvoorbeeld het soort DTO genereren dat BOB kan genereren. We sturen ze de ID, maar ze slaan deze niet op omdat ze deze niet nodig hebben, en vanuit het oogpunt van het starten van het event-busproces is dit veld verplicht.

Als we voor event-bus een regel introduceren dat dit veld verplicht is, dan zijn we genoodzaakt om aanvullende validatieregels in te stellen in de BOB of in de start event handler. Validatie begint zich door de hele service te verspreiden - dit is niet erg handig.

Een ander probleem is de verleiding tot stapsgewijze ontwikkeling. Er wordt ons verteld dat er iets aan de gebeurtenis moet worden toegevoegd, en misschien, als we erover nadenken, had het een aparte gebeurtenis moeten zijn. Maar in ons schema is een afzonderlijk evenement een apart onderwerp. Een apart onderwerp is het hele proces dat ik hierboven heb beschreven. De ontwikkelaar komt in de verleiding om simpelweg een ander veld aan het JSON-schema toe te voegen en dit opnieuw te genereren.

Bij restituties kwamen we binnen een half jaar uit bij evenementen. We hadden één meta-gebeurtenis genaamd terugbetalingsupdate, die een typeveld had dat beschrijft wat deze update eigenlijk was. Hierdoor hadden we “prachtige” switches met validators die ons vertelden hoe we deze gebeurtenis met dit type konden valideren.

Versiebeheer van gebeurtenissen

Om berichten in Kafka te valideren kunt u gebruik maken van euro, maar het was nodig om er onmiddellijk op te gaan liggen en Confluent te gebruiken. In ons geval moeten we voorzichtig zijn met versiebeheer. Het zal niet altijd mogelijk zijn om berichten uit het replicatielogboek opnieuw te lezen omdat het model “verlaten” is. In principe blijkt het zo te zijn om versies zo te bouwen dat het model achterwaarts compatibel is: maak bijvoorbeeld een veld tijdelijk optioneel. Als de verschillen te groot zijn, beginnen we in een nieuw onderwerp te schrijven en verplaatsen we klanten wanneer ze klaar zijn met het lezen van het oude onderwerp.

Gegarandeerde leesvolgorde van partities

Onderwerpen binnen Kafka zijn onderverdeeld in partities. Dit is niet erg belangrijk bij het ontwerpen van entiteiten en uitwisselingen, maar het is wel belangrijk bij het beslissen hoe we deze moeten consumeren en schalen.

Normaal gesproken schrijf je één onderwerp in Kafka. Standaard wordt één partitie gebruikt en gaan alle berichten in dit onderwerp ernaartoe. En de consument leest deze berichten dus opeenvolgend. Laten we zeggen dat we het systeem nu moeten uitbreiden zodat berichten door twee verschillende consumenten kunnen worden gelezen. Als u bijvoorbeeld een sms verzendt, kunt u Kafka vertellen een extra partitie te maken, en Kafka begint de berichten in twee delen te splitsen: de helft hier, de helft hier.

Hoe verdeelt Kafka ze? Elk bericht heeft een hoofdtekst (waarin we JSON opslaan) en een sleutel. U kunt aan deze sleutel een hash-functie koppelen, die bepaalt op welke partitie het bericht terechtkomt.

In ons geval met restituties is dit belangrijk: als we twee partities nemen, bestaat de kans dat een parallelle consument de tweede gebeurtenis vóór de eerste verwerkt en er problemen ontstaan. De hashfunctie zorgt ervoor dat berichten met dezelfde sleutel op dezelfde partitie terechtkomen.

Gebeurtenissen versus opdrachten

Dit is een ander probleem dat we tegenkwamen. Gebeurtenis is een bepaalde gebeurtenis: we zeggen dat er ergens iets is gebeurd (iets_gebeurd), bijvoorbeeld dat een artikel is geannuleerd of dat er een terugbetaling heeft plaatsgevonden. Als iemand naar deze gebeurtenissen luistert, wordt volgens 'item geannuleerd' de restitutie-entiteit aangemaakt en wordt 'restitutie plaatsgevonden' ergens in de instellingen geschreven.

Maar als je evenementen ontwerpt, wil je ze meestal niet tevergeefs schrijven - je vertrouwt erop dat iemand ze zal lezen. De verleiding is groot om niet te schrijven dat er iets is gebeurd (item_canceled, restitutie_refunded), maar dat er iets_moet_worden_gedaan. Het artikel staat bijvoorbeeld klaar om te worden geretourneerd.

Enerzijds suggereert het hoe het evenement gebruikt zal worden. Aan de andere kant klinkt het veel minder als een normale evenementnaam. Trouwens, het is niet ver hier vandaan naar het do_something commando. Maar je hebt geen garantie dat iemand dit evenement heeft gelezen; en als je het leest, dan lees je het met succes; en als je het met succes hebt gelezen, dan heb je iets gedaan, en dat iets was succesvol. Op het moment dat een gebeurtenis iets wordt, wordt feedback noodzakelijk, en dat is een probleem.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Als u bij asynchrone uitwisseling in RabbitMQ het bericht leest, gaat u naar http en krijgt u een antwoord - in ieder geval dat het bericht is ontvangen. Wanneer je naar Kafka schrijft, is er een bericht dat je naar Kafka hebt geschreven, maar je weet niets over hoe het is verwerkt.

Daarom moesten we in ons geval een responsgebeurtenis introduceren en monitoring instellen, zodat als er zoveel gebeurtenissen werden verzonden, na die en die tijd hetzelfde aantal responsgebeurtenissen zou arriveren. Als dit niet gebeurt, lijkt er iets mis te zijn gegaan. Als we bijvoorbeeld de gebeurtenis ‘item_ready_to_refund’ hebben verzonden, verwachten we dat er een terugbetaling zal plaatsvinden, dat het geld wordt teruggestuurd naar de klant en dat de gebeurtenis ‘money_refunded’ naar ons wordt verzonden. Maar dit is niet zeker, dus monitoring is nodig.

nuances

Er is een vrij voor de hand liggend probleem: als je een onderwerp achter elkaar leest en je hebt een slechte boodschap, zal de consument vallen en kom je niet verder. Jij hebt nodig stop alle consumenten, voer de offset verder uit om verder te lezen.

We wisten ervan, we rekenden erop, en toch gebeurde het. En dit gebeurde omdat de gebeurtenis geldig was vanuit het oogpunt van events-bus, de gebeurtenis was geldig vanuit het oogpunt van de applicatievalidator, maar niet geldig vanuit het oogpunt van PostgreSQL, omdat in ons ene systeem MySQL met UNSIGNED INT, en in de nieuw geschreven versie had het systeem PostgreSQL alleen met INT. Zijn maat is iets kleiner en de ID paste niet. Symfony stierf op een uitzondering na. We hebben natuurlijk de uitzondering ondervonden omdat we erop vertrouwden en deze compensatie gingen vastleggen, maar daarvoor wilden we de probleemteller verhogen, omdat het bericht niet succesvol werd verwerkt. De tellers in dit project bevinden zich ook in de database, en Symfony heeft de communicatie met de database al gesloten, en de tweede uitzondering heeft het hele proces stopgezet zonder de kans om offset vast te leggen.

De service lag enige tijd stil - bij Kafka valt dit gelukkig mee, want de berichten blijven staan. Wanneer het werk is hersteld, kunt u ze verder lezen. Het is comfortabel.

Kafka heeft de mogelijkheid om via gereedschap een willekeurige offset in te stellen. Maar om dit te doen, moet je alle consumenten tegenhouden - in ons geval een aparte release voorbereiden waarin er geen consumenten zullen zijn, herimplementaties. Vervolgens kun je in Kafka de offset verschuiven door middel van gereedschap, en de boodschap zal overkomen.

Nog een nuance - replicatielogboek versus rdkafka.so - houdt verband met de specifieke kenmerken van ons project. We gebruiken PHP, en in PHP communiceren alle bibliotheken in de regel met Kafka via de rdkafka.so-repository, en dan is er een soort wrapper. Misschien zijn dit onze persoonlijke moeilijkheden, maar het bleek dat het simpelweg herlezen van een stukje van wat we al hadden gelezen niet zo eenvoudig is. Over het algemeen waren er softwareproblemen.

Terugkomend op de details van het werken met partities: het staat precies in de documentatie consumenten >= onderwerppartities. Maar ik kwam er veel later achter dan ik had gewild. Als je wilt schalen en twee consumenten wilt hebben, heb je minimaal twee partities nodig. Dat wil zeggen, als u één partitie had waarin zich 20 berichten hadden verzameld en u een nieuwe partitie maakte, zal het aantal berichten niet snel gelijk worden gemaakt. Om twee parallelle consumenten te hebben, moet u daarom met partities omgaan.

controle

Ik denk dat de manier waarop we dit monitoren nog duidelijker zal maken welke problemen er in de bestaande aanpak zitten.

We berekenen bijvoorbeeld hoeveel producten in de database onlangs hun status hebben gewijzigd, en dienovereenkomstig hadden er gebeurtenissen moeten plaatsvinden op basis van deze wijzigingen, en we sturen dit aantal naar ons monitoringsysteem. Vervolgens krijgen we van Kafka het tweede getal: hoeveel evenementen er daadwerkelijk zijn opgenomen. Het is duidelijk dat het verschil tussen deze twee getallen altijd nul moet zijn.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Daarnaast moet je monitoren hoe het met de producent gaat, of events-bus berichten heeft ontvangen en hoe het met de consument gaat. In de onderstaande grafieken doet de Refund Tool het bijvoorbeeld goed, maar BOB heeft duidelijk enkele problemen (blauwe pieken).

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Ik noemde de vertraging bij de consumentengroep al. Grofweg is dit het aantal ongelezen berichten. Over het algemeen werken onze consumenten snel, dus de vertraging is meestal 0, maar soms kan er een kortetermijnpiek optreden. Kafka kan dit kant-en-klaar doen, maar je moet wel een bepaald interval instellen.

Er is een project Holdie u meer informatie over Kafka geeft. Het maakt eenvoudigweg gebruik van de consumentengroep-API om de status te geven van hoe het met deze groep gaat. Naast OK en Failed is er een waarschuwing en kunt u ontdekken dat uw consumenten het productietempo niet aankunnen - ze hebben geen tijd om te proeflezen wat er staat. Het systeem is behoorlijk slim en gemakkelijk te gebruiken.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

Zo ziet het API-antwoord eruit. Hier is de groep bob-live-fifa, partitie restitutie.update.v1, status OK, lag 0 - de laatste definitieve offset zo en zo.

Ervaring met het ontwikkelen van de Refund Tool-service met een asynchrone API op Kafka

controle bijgewerkt_at SLA (vastgelopen) Ik vermeldde het al. Het product is bijvoorbeeld gewijzigd naar de status dat het gereed is voor retour. We installeren Cron, die zegt dat als dit object binnen 5 minuten niet is terugbetaald (we geven heel snel geld terug via betalingssystemen), er iets absoluut mis is gegaan, en dit is zeker een reden voor ondersteuning. Daarom nemen we eenvoudigweg Cron, die dergelijke dingen leest, en als ze groter zijn dan 0, stuurt hij een waarschuwing.

Samenvattend: het gebruik van evenementen is handig wanneer:

  • informatie is nodig voor verschillende systemen;
  • het resultaat van de verwerking is niet van belang;
  • er zijn weinig evenementen of kleine evenementen.

Het lijkt erop dat het artikel een heel specifiek onderwerp heeft: asynchrone API op Kafka, maar in verband daarmee zou ik veel dingen tegelijk willen aanbevelen.
Eerst, volgende HighLoad ++ we moeten wachten tot november, in april komt er een versie in Sint-Petersburg en in juni praten we over hoge belastingen in Novosibirsk.
Ten tweede is de auteur van het rapport, Sergei Zaika, lid van de Programmacommissie van onze nieuwe conferentie over kennismanagement KennisConf. De conferentie duurt een dag en vindt plaats op 26 april, maar het programma is zeer intens.
En dat zal in mei zijn PHP Rusland и RIT++ (inclusief DevOpsConf) - u kunt daar ook uw onderwerp voorstellen, over uw ervaringen praten en klagen over uw gevulde kegels.

Bron: www.habr.com

Voeg een reactie