Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Hva kan tvinge et så stort selskap som Lamoda, med en strømlinjeformet prosess og dusinvis av sammenkoblede tjenester, til å endre tilnærmingen sin betydelig? Motivasjon kan være helt forskjellig: fra lovgivende til ønsket om å eksperimentere som ligger i alle programmerere.

Men dette betyr ikke at du ikke kan regne med tilleggsfordeler. Sergey Zaika vil fortelle deg nøyaktig hva du kan vinne hvis du implementerer det hendelsesdrevne API på Kafka (fewald). Det vil også definitivt bli snakk om store skudd og interessante funn – eksperimentet klarer seg ikke uten dem.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Ansvarsfraskrivelse: Denne artikkelen er basert på materiale fra et møte som Sergey holdt i november 2018 på HighLoad++. Lamodas live-erfaring med å jobbe med Kafka trakk til seg lyttere ikke mindre enn andre reportasjer på timeplanen. Vi synes dette er et utmerket eksempel på at du alltid kan og bør finne likesinnede, og arrangørene av HighLoad++ vil fortsette å prøve å skape en atmosfære som bidrar til dette.

Om prosessen

Lamoda er en stor e-handelsplattform som har eget kontaktsenter, leveringstjeneste (og mange tilknyttede selskaper), et fotostudio, et enormt lager, og alt dette kjører på egen programvare. Det finnes dusinvis av betalingsmetoder, b2b-partnere som kan bruke noen eller alle disse tjenestene og ønsker å vite oppdatert informasjon om produktene deres. I tillegg opererer Lamoda i tre land foruten den russiske føderasjonen og alt er litt annerledes der. Totalt er det trolig mer enn hundre måter å konfigurere en ny ordre på, som må behandles på sin egen måte. Alt dette fungerer ved hjelp av dusinvis av tjenester som noen ganger kommuniserer på ikke-opplagte måter. Det er også et sentralt system hvis hovedansvar er ordrestatuser. Vi kaller henne BIR, jeg jobber med henne.

Refusjonsverktøy med hendelsesdrevet API

Ordet hendelsesdrevet er ganske knasende, litt nærmere vil vi definere mer detaljert hva som menes med dette. Jeg starter med konteksten der vi bestemte oss for å prøve den hendelsesdrevne API-tilnærmingen i Kafka.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

I en hvilken som helst butikk, i tillegg til bestillinger som kundene betaler for, er det tider hvor butikken er pålagt å returnere penger fordi produktet ikke passet kunden. Dette er en relativt kort prosess: vi avklarer informasjonen om nødvendig og overfører pengene.

Men returen ble mer komplisert på grunn av endringer i lovgivningen, og vi måtte implementere en egen mikrotjeneste for det.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Vår motivasjon:

  1. Lov FZ-54 – Kort fortalt krever loven rapportering til skattekontoret om hver pengetransaksjon, det være seg en retur eller en kvittering, innen en ganske kort SLA på noen få minutter. Vi som e-handelsselskap utfører ganske mange operasjoner. Teknisk sett betyr dette nytt ansvar (og derfor en ny tjeneste) og forbedringer i alle involverte systemer.
  2. BIR delt er et internt prosjekt i selskapet for å frita BOB fra et stort antall ikke-kjerneansvar og redusere dens generelle kompleksitet.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Dette diagrammet viser de viktigste Lamoda-systemene. Nå er de fleste av dem flere en konstellasjon av 5-10 mikrotjenester rundt en krympende monolitt. De vokser sakte, men vi prøver å gjøre dem mindre, fordi å distribuere fragmentet som er valgt i midten er skummelt - vi kan ikke la det falle. Vi er tvunget til å reservere alle børser (piler) og ta hensyn til at noen av dem kan vise seg å være utilgjengelige.

BOB har også ganske mange sentraler: betalingssystemer, leveringssystemer, varslingssystemer osv.

Teknisk BIR er:

  • ~150k linjer med kode + ~100k linjer med tester;
  • php7.2 + Zend 1 & Symfony Components 3;
  • >100 APIer og ~50 utgående integrasjoner;
  • 4 land med egen forretningslogikk.

Å distribuere BOB er dyrt og smertefullt, mengden kode og problemer det løser er slik at ingen kan sette alt inn i hodet. Generelt er det mange grunner til å forenkle det.

Returprosess

I første omgang er to systemer involvert i prosessen: BIR og Betaling. Nå dukker det opp to til:

  • Fiskaliseringstjeneste, som skal ta seg av problemer med fiskalisering og kommunikasjon med eksterne tjenester.
  • Refund Tool, som ganske enkelt inneholder nye børser for ikke å blåse opp BIR.

Nå ser prosessen slik ut:

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

  1. BIR mottar en forespørsel om refusjon.
  2. BOB snakker om dette refusjonsverktøyet.
  3. Tilbakebetalingsverktøyet sier til Betaling: "Returner pengene."
  4. Betaling gir pengene tilbake.
  5. Refund Tool og BOB synkroniserer statuser med hverandre, for foreløpig trenger de det begge. Vi er ennå ikke klare til å gå helt over til Refund Tool, siden BOB har UI, rapporter for regnskap og generelt mye data som ikke kan overføres så enkelt. Du må sitte på to stoler.
  6. Forespørselen om skatteplikt forsvinner.

Som et resultat laget vi en slags eventbuss på Kafka – event-buss, som alt startet på. Hurra, nå har vi et enkelt feilpunkt (sarkasme).

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Fordeler og ulemper er ganske åpenbare. Vi laget en buss, noe som betyr at nå er alle tjenester avhengig av den. Dette forenkler designet, men introduserer et enkelt feilpunkt i systemet. Kafka vil krasje, prosessen vil stoppe.

Hva er et hendelsesdrevet API

Et godt svar på dette spørsmålet er i rapporten av Martin Fowler (GOTO 2017) "De mange betydningene av hendelsesdrevet arkitektur".

Kort hva vi gjorde:

  1. Avslutt alle asynkrone sentraler via lagring av hendelser. I stedet for å informere enhver interessert forbruker om en statusendring over nettverket, skriver vi en begivenhet om en statusendring til en sentralisert lagring, og forbrukere som er interessert i emnet leser alt som dukker opp derfra.
  2. Hendelsen i dette tilfellet er et varsel (varslinger) at noe har endret seg et sted. For eksempel har ordrestatusen endret seg. En forbruker som er interessert i noen data som følger med statusendringen som ikke er inkludert i varselet, kan finne ut statusen selv.
  3. Det maksimale alternativet er fullverdig hendelseskilde, statsoverføring, i hvilket tilfelle inneholder all informasjon som er nødvendig for behandling: hvor den kom fra og hvilken status den gikk til, nøyaktig hvordan dataene endret seg osv. Spørsmålet er bare gjennomførbarheten og mengden informasjon du har råd til å lagre.

Som en del av lanseringen av refusjonsverktøyet brukte vi det tredje alternativet. Denne forenklet hendelsesbehandling siden det ikke var behov for å trekke ut detaljert informasjon, pluss at det eliminerte scenariet der hver ny hendelse genererer en serie med oppklarende hent-forespørsler fra forbrukere.

Refusjonsverktøytjeneste ikke lastet, så Kafka er det mer en smak av pennen enn en nødvendighet. Jeg tror ikke at hvis refusjonstjenesten ble et høybelastningsprosjekt, ville bedriften være fornøyd.

Asynkron utveksling SOM DEN ER

For asynkrone utvekslinger bruker PHP-avdelingen vanligvis RabbitMQ. Vi samlet inn dataene for forespørselen, satte dem i kø, og forbrukeren av samme tjeneste leste den og sendte den (eller sendte den ikke). For selve APIen bruker Lamoda Swagger aktivt. Vi designer et API, beskriver det i Swagger, og genererer klient- og serverkode. Vi bruker også en litt forbedret JSON RPC 2.0.

Noen steder brukes ESB-busser, noen lever på activeMQ, men generelt sett, RabbitMQ - standard.

Asynkron utveksling TO BE

Ved utforming av utveksling via event-buss kan en analogi spores. Vi beskriver på samme måte fremtidig datautveksling gjennom hendelsesstrukturbeskrivelser. Yaml-formatet, vi måtte gjøre kodegenereringen selv, generatoren lager DTO-er i henhold til spesifikasjonen og lærer klienter og servere å jobbe med dem. Generasjon går inn på to språk - golang og php. Dette bidrar til å holde bibliotekene konsistente. Generatoren er skrevet i golang, som er grunnen til at den fikk navnet gogi.

Event-sourcing på Kafka er en typisk ting. Det finnes en løsning fra hovedbedriftsversjonen av Kafka Confluent nakadi, en løsning fra våre domenebrødre Zalando. Vår motivasjon til å begynne med vanilje Kafka – dette betyr å la løsningen være fri til vi endelig bestemmer oss for om vi skal bruke den overalt, og også gi oss selv handlingsrom og forbedringer: vi ønsker støtte for våre JSON RPC 2.0, generatorer for to språk og la oss se hva mer.

Det er ironisk at selv i et så lykkelig tilfelle, når det er en omtrent lik virksomhet, Zalando, som har laget en omtrent lik løsning, kan vi ikke bruke den effektivt.

Det arkitektoniske mønsteret ved lanseringen er som følger: vi leser direkte fra Kafka, men skriver kun gjennom event-buss. Det er mye klart for lesing i Kafka: meglere, balansere, og det er mer eller mindre klart for horisontal skalering, dette ønsket jeg å beholde. Vi ønsket å fullføre opptaket gjennom én Gateway aka Events-bus, og her er hvorfor.

Event-buss

Eller en eventbuss. Dette er rett og slett en statsløs http-gateway, som tar på seg flere viktige roller:

  • Produserer validering — vi sjekker at arrangementene oppfyller våre spesifikasjoner.
  • Event master system, det vil si at dette er det viktigste og eneste systemet i selskapet som svarer på spørsmålet om hvilke hendelser med hvilke strukturer som anses som gyldige. Validering involverer ganske enkelt datatyper og opptegnelser for å spesifisere innhold strengt.
  • Hash funksjon for sharding - Kafka-meldingsstrukturen er nøkkelverdi, og ved å bruke hashen av nøkkelen beregnes det hvor den skal plasseres.

Hvorfor

Vi jobber i en stor bedrift med en strømlinjeformet prosess. Hvorfor endre noe? Dette er et eksperiment, og vi forventer å høste flere fordeler.

1:n+1-utvekslinger (én til mange)

Kafka gjør det veldig enkelt å koble nye forbrukere til API.

La oss si at du har en katalog som du trenger for å holde deg oppdatert i flere systemer samtidig (og i noen nye). Tidligere oppfant vi en pakke som implementerte set-API, og hovedsystemet ble informert om forbrukeradresser. Nå sender mastersystemet oppdateringer til emnet, og alle som er interessert leser det. Et nytt system har dukket opp - vi har registrert det for emnet. Ja, også bunt, men enklere.

Når det gjelder refusjonsverktøy, som er en del av BIR, er det praktisk for oss å holde dem synkronisert gjennom Kafka. Betaling sier at pengene ble returnert: BIR, RT fikk vite om dette, endret status, Fiscalization Service fant ut om dette og utstedte en sjekk.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Vi har planer om å opprette en enhetlig varslingstjeneste som vil varsle klienten om nyheter angående hans ordre/retur. Nå er dette ansvaret spredt mellom systemene. Det vil være nok for oss å lære varslingstjenesten å fange opp relevant informasjon fra Kafka og svare på den (og deaktivere disse varslene i andre systemer). Ingen nye direkte utvekslinger vil være nødvendig.

Data drevet

Informasjon mellom systemer blir gjennomsiktig - uansett hvilken "bloody enterprise" du har og uansett hvor fyldig etterslep er. Lamoda har en Data Analytics-avdeling som samler inn data fra systemer og setter dem i en gjenbrukbar form, både for bedrifter og for intelligente systemer. Kafka lar deg raskt gi dem mye data og holde denne informasjonsflyten oppdatert.

Replikeringslogg

Meldinger forsvinner ikke etter å ha blitt lest, som i RabbitMQ. Når en hendelse inneholder nok informasjon for behandling, har vi en historikk over nylige endringer i objektet, og, om ønskelig, muligheten til å bruke disse endringene.

Lagringsperioden for replikeringsloggen avhenger av intensiteten av å skrive til dette emnet; Kafka lar deg fleksibelt sette grenser for lagringstid og datavolum. For intensive emner er det viktig at alle forbrukere rekker å lese informasjonen før den forsvinner, selv ved kortvarig ubrukbarhet. Det er vanligvis mulig å lagre data for enheter av dager, som er ganske nok for støtte.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Deretter en liten gjenfortelling av dokumentasjonen, for de som ikke er kjent med Kafka (bildet er også fra dokumentasjonen)

AMQP har køer: vi skriver meldinger til en kø for forbrukeren. Vanligvis behandles én kø av ett system med samme forretningslogikk. Hvis du trenger å varsle flere systemer, kan du lære applikasjonen å skrive til flere køer eller konfigurere utveksling med fanout-mekanismen, som kloner dem selv.

Kafka har en lignende abstraksjon Tema, der du skriver meldinger, men de forsvinner ikke etter lesing. Som standard, når du kobler til Kafka, mottar du alle meldinger og har muligheten til å lagre der du slapp. Det vil si at du leser sekvensielt, du kan ikke merke meldingen som lest, men lagre id-en som du så kan fortsette å lese fra. Id-en du satte deg på kalles offset, og mekanismen er commit offset.

Følgelig kan forskjellig logikk implementeres. For eksempel har vi BIR i 4 tilfeller for forskjellige land - Lamoda er i Russland, Kasakhstan, Ukraina, Hviterussland. Siden de er distribuert separat, har de litt forskjellige konfigurasjoner og sin egen forretningslogikk. Vi angir i meldingen hvilket land det refereres til. Hver BOB-forbruker i hvert land leser med en annen gruppe-ID, og ​​hvis meldingen ikke gjelder dem, hopper de over den, dvs. begår umiddelbart offset +1. Hvis det samme emnet leses av betalingstjenesten vår, gjør det det med en egen gruppe, og derfor krysser ikke forskyvninger.

Krav til arrangement:

  • Datafullstendighet. Jeg vil gjerne at arrangementet har nok data til at det kan behandles.

  • Integritet. Vi delegerer til Events-bus bekreftelsen på at hendelsen er konsistent og at den kan behandle den.
  • Rekkefølgen er viktig. Ved retur tvinges vi til å jobbe med historien. Med varslinger er ikke bestillingen viktig, dersom det er homogene varsler vil e-posten være den samme uavhengig av hvilken bestilling som kom først. Ved refusjon er det en klar prosess; endrer vi rekkefølgen vil det oppstå unntak, refusjonen blir ikke opprettet eller behandlet - vi havner i en annen status.
  • Konsistens. Vi har en butikk, og nå lager vi arrangementer i stedet for et API. Vi trenger en måte å raskt og billig overføre informasjon om nye arrangementer og endringer til eksisterende til våre tjenester. Dette oppnås gjennom en felles spesifikasjon i et eget git-lager og kodegeneratorer. Derfor er klienter og servere i ulike tjenester koordinert.

Kafka i Lamoda

Vi har tre Kafka-installasjoner:

  1. Tømmerstokker;
  2. FoU;
  3. Event-buss.

I dag snakker vi bare om det siste punktet. Hos events-bus har vi ikke veldig store installasjoner - 3 meglere (servere) og kun 27 emner. Som regel er ett tema én prosess. Men dette er et subtilt poeng, og vi skal berøre det nå.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Over er rps-grafen. Refusjonsprosessen er merket med en turkis linje (ja, den på X-aksen), og den rosa linjen er innholdsoppdateringsprosessen.

Lamoda-katalogen inneholder millioner av produkter, og dataene oppdateres hele tiden. Noen samlinger går av moten, nye slippes for å erstatte dem, og nye modeller dukker stadig opp i katalogen. Vi prøver å forutsi hva som vil være interessant for kundene våre i morgen, så vi kjøper stadig nye ting, fotograferer dem og oppdaterer vitrineskapet.

Rosa topper er produktoppdateringer, det vil si endringer i produkter. Det kan sees at gutta tok bilder, tok bilder, og så igjen! — lastet inn en pakke med hendelser.

Lamoda Events brukssaker

Vi bruker den konstruerte arkitekturen for følgende operasjoner:

  • Returstatussporing: handlingsfremmende oppfordring og statussporing fra alle involverte systemer. Betaling, statuser, fiskalisering, varsler. Her testet vi tilnærmingen, laget verktøy, samlet alle feilene, skrev dokumentasjon og fortalte kollegene våre hvordan de skulle bruke den.
  • Oppdatering av produktkort: konfigurasjon, metadata, egenskaper. Ett system leser (som vises), og flere skriver.
  • E-post, push og sms: bestillingen er hentet, bestillingen er kommet, returen er akseptert osv., det er mange av dem.
  • Lager, lagerfornyelse — kvantitativ oppdatering av varer, bare tall: ankomst på lageret, retur. Det er nødvendig at alle systemer knyttet til reservasjon av varer opererer med de nyeste dataene. For øyeblikket er aksjeoppdateringssystemet ganske komplekst; Kafka vil forenkle det.
  • Dataanalyse (FoU-avdelingen), ML-verktøy, analyser, statistikk. Vi ønsker at informasjon skal være transparent – ​​Kafka er godt egnet til dette.

Nå den mer interessante delen om de store ujevnhetene og interessante funnene som har skjedd de siste seks månedene.

Designproblemer

La oss si at vi ønsker å gjøre en ny ting – for eksempel overføre hele leveringsprosessen til Kafka. Nå er en del av prosessen implementert i Ordrebehandling i BIR. Det ligger en statusmodell bak overføring av en ordre til leveringstjenesten, flytting til et mellomlager og så videre. Det er en hel monolitt, til og med to, pluss en haug med APIer dedikert til levering. De vet mye mer om levering.

Dette ser ut til å være like områder, men Ordrebehandlingen i BIR og Shipping System har ulike statuser. For eksempel sender noen budtjenester ikke mellomstatuser, men bare de siste: "levert" eller "tapt". Andre, tvert imot, rapporterer svært detaljert om bevegelse av varer. Alle har sine egne valideringsregler: for noen er e-posten gyldig, noe som betyr at den vil bli behandlet; for andre er det ikke gyldig, men bestillingen vil likevel bli behandlet fordi det er et telefonnummer for kontakt, og noen vil si at en slik bestilling ikke vil bli behandlet i det hele tatt.

Data strøm

I Kafkas tilfelle oppstår spørsmålet om organisering av dataflyten. Denne oppgaven innebærer å velge en strategi basert på flere punkter; la oss gå gjennom dem alle.

I ett emne eller i forskjellige?

Vi har en hendelsesspesifikasjon. I BIR skriver vi at en slik og en bestilling må leveres, og angir: ordrenummeret, dets sammensetning, noen SKU-er og strekkoder osv. Når varene kommer til lageret vil leveransen kunne motta statuser, tidsstempler og alt som skal til. Men da ønsker vi å motta oppdateringer på disse dataene i BIR. Vi har en omvendt prosess for å motta data fra levering. Er dette samme hendelse? Eller er dette en egen meningsutveksling som fortjener et eget tema?

Mest sannsynlig vil de være veldig like, og fristelsen til å lage ett emne er ikke ubegrunnet, fordi et eget emne betyr separate forbrukere, separate konfigurasjoner, en egen generasjon av alt dette. Men ikke et faktum.

Nytt felt eller nytt arrangement?

Men hvis du bruker de samme hendelsene, oppstår et annet problem. For eksempel kan ikke alle leveringssystemer generere den typen DTO som BOB kan generere. Vi sender dem ID-en, men de lagrer den ikke fordi de ikke trenger den, og fra synspunktet om å starte event-bus-prosessen, er dette feltet obligatorisk.

Hvis vi introduserer en regel for event-bus om at dette feltet er påkrevd, så er vi tvunget til å sette ytterligere valideringsregler i BIR eller i start event handler. Validering begynner å spre seg gjennom tjenesten - dette er ikke veldig praktisk.

Et annet problem er fristelsen til inkrementell utvikling. Vi får beskjed om at noe må legges til arrangementet, og kanskje, hvis vi tenker oss om, burde det vært et eget arrangement. Men i vårt opplegg er et eget arrangement et eget tema. Et eget emne er hele prosessen som jeg beskrev ovenfor. Utvikleren er fristet til å legge til et annet felt i JSON-skjemaet og regenerere det.

Ved refusjon kom vi frem til hendelser om et halvt år. Vi hadde en meta-hendelse kalt refusjonsoppdatering, som hadde et typefelt som beskrev hva denne oppdateringen faktisk var. På grunn av dette hadde vi "fantastiske" brytere med validatorer som fortalte oss hvordan vi kunne validere denne hendelsen med denne typen.

Eventversjon

For å validere meldinger i Kafka kan du bruke Avro, men det var nødvendig å umiddelbart legge seg på den og bruke Confluent. I vårt tilfelle må vi være forsiktige med versjonering. Det vil ikke alltid være mulig å lese meldinger fra replikeringsloggen på nytt fordi modellen har "forlatt". I utgangspunktet viser det seg å bygge versjoner slik at modellen er bakoverkompatibel: for eksempel gjør et felt midlertidig valgfritt. Hvis forskjellene er for sterke, begynner vi å skrive inn et nytt emne, og overfører klienter når de er ferdige med å lese det gamle.

Garantert leserekkefølge av partisjoner

Emner inne i Kafka er delt inn i partisjoner. Dette er ikke veldig viktig mens vi designer enheter og børser, men det er viktig når vi skal bestemme hvordan vi skal konsumere og skalere det.

I det vanlige tilfellet skriver du ett emne i Kafka. Som standard brukes én partisjon, og alle meldinger i dette emnet går til den. Og forbrukeren leser følgelig disse meldingene sekvensielt. La oss si at nå må vi utvide systemet slik at meldinger leses av to forskjellige forbrukere. Hvis du for eksempel sender SMS, så kan du fortelle Kafka å lage en ekstra partisjon, og Kafka vil begynne å dele meldingene i to deler - halvparten her, halvparten her.

Hvordan deler Kafka dem? Hver melding har en body (der vi lagrer JSON) og en nøkkel. Du kan legge ved en hash-funksjon til denne nøkkelen, som vil avgjøre hvilken partisjon meldingen skal gå inn i.

I vårt tilfelle med refusjon er dette viktig, hvis vi tar to partisjoner, så er det en sjanse for at en parallellforbruker vil behandle den andre hendelsen før den første og det blir trøbbel. Hash-funksjonen sørger for at meldinger med samme nøkkel havner i samme partisjon.

Hendelser vs kommandoer

Dette er et annet problem vi møtte. Hendelse er en bestemt hendelse: vi sier at noe skjedde et sted (noe_har skjedd), for eksempel at en vare ble kansellert eller en refusjon skjedde. Hvis noen lytter til disse hendelsene, vil refusjonsenheten bli opprettet i henhold til "elementet kansellert", og "refusjon skjedde" vil bli skrevet et sted i oppsettene.

Men vanligvis, når du designer hendelser, vil du ikke skrive dem forgjeves - du stoler på det faktum at noen vil lese dem. Det er en stor fristelse å skrive ikke noe_har skjedd (item_cancelled, refund_refunded), men noe_burde_gjøres. For eksempel er varen klar til å bli returnert.

På den ene siden antyder det hvordan arrangementet skal brukes. På den annen side høres det mye mindre ut som et vanlig hendelsesnavn. Dessuten er det ikke langt herfra til do_something-kommandoen. Men du har ingen garanti for at noen leser denne hendelsen; og hvis du leser den, så leser du den med hell; og hvis du leste den vellykket, så gjorde du noe, og at noe var vellykket. I det øyeblikket en hendelse blir gjøre_noe, blir tilbakemelding nødvendig, og det er et problem.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

I asynkron utveksling i RabbitMQ, når du leser meldingen, går du til http, du har et svar - i det minste at meldingen ble mottatt. Når du skriver til Kafka, er det en melding du skrev til Kafka, men du vet ingenting om hvordan den ble behandlet.

Derfor måtte vi i vårt tilfelle innføre en responshendelse og sette opp overvåking slik at dersom det ble sendt så mange hendelser, skulle det komme like mange responshendelser etter slik og slik tid. Hvis dette ikke skjer, ser det ut til at noe har gått galt. For eksempel, hvis vi sendte «item_ready_to_refund»-hendelsen, forventer vi at en refusjon vil bli opprettet, pengene vil bli returnert til kunden, og «money_refunded»-hendelsen vil bli sendt til oss. Men dette er ikke sikkert, så overvåking er nødvendig.

nyanser

Det er et ganske åpenbart problem: hvis du leser fra et emne sekvensielt, og du har et dårlig budskap, vil forbrukeren falle, og du kommer ikke lenger. Du trenger stoppe alle forbrukere, commit offset ytterligere for å fortsette å lese.

Vi visste om det, vi regnet med det, og likevel skjedde det. Og dette skjedde fordi arrangementet var gyldig fra synspunktet til hendelser-buss, arrangementet var gyldig fra synspunktet til applikasjonsvalidatoren, men det var ikke gyldig fra synspunktet til PostgreSQL, fordi i vårt ene system MySQL med USIGNERT INT, og i den nyskrevne hadde systemet PostgreSQL bare med INT. Størrelsen hans er litt mindre, og ID-en passet ikke. Symfony døde med et unntak. Vi fanget selvfølgelig unntaket fordi vi stolte på det og skulle utføre denne forskyvningen, men før det ønsket vi å øke problemtelleren, siden meldingen ble behandlet uten hell. Tellerne i dette prosjektet er også i databasen, og Symfony har allerede lukket kommunikasjonen med databasen, og det andre unntaket drepte hele prosessen uten mulighet til å foreta offset.

Tjenesten ble liggende en stund - heldigvis, med Kafka er dette ikke så ille, fordi meldingene forblir. Når arbeidet er gjenopprettet, kan du lese dem ferdig. Det er behagelig.

Kafka har muligheten til å sette en vilkårlig forskyvning gjennom verktøy. Men for å gjøre dette, må du stoppe alle forbrukere - i vårt tilfelle, forbered en egen utgivelse der det ikke vil være noen forbrukere, omplasseringer. Så i Kafka kan du flytte offset gjennom verktøy, og meldingen vil gå gjennom.

En annen nyanse - replikeringslogg vs rdkafka.so - er relatert til detaljene i prosjektet vårt. Vi bruker PHP, og i PHP kommuniserer som regel alle biblioteker med Kafka gjennom rdkafka.so-depotet, og da er det en slags wrapper. Kanskje dette er våre personlige vanskeligheter, men det viste seg at det ikke er så lett å bare lese en del av det vi allerede hadde lest på nytt. Generelt var det programvareproblemer.

For å gå tilbake til detaljene ved å jobbe med partisjoner, er det skrevet rett i dokumentasjonen forbrukere >= emnepartisjoner. Men jeg fant ut om dette mye senere enn jeg ville ha ønsket. Hvis du vil skalere og ha to forbrukere, trenger du minst to partisjoner. Det vil si, hvis du hadde en partisjon der 20 tusen meldinger hadde samlet seg, og du laget en ny, vil ikke antallet meldinger bli utlignet snart. Derfor, for å ha to parallelle forbrukere, må du forholde deg til partisjoner.

overvåking

Jeg tror måten vi overvåker det på vil bli enda tydeligere hvilke problemer det er i den eksisterende tilnærmingen.

For eksempel beregner vi hvor mange produkter i databasen som nylig har endret status, og følgelig skulle hendelser ha skjedd basert på disse endringene, og vi sender dette tallet til vårt overvåkingssystem. Så fra Kafka får vi det andre tallet, hvor mange hendelser som faktisk ble registrert. Det er klart at forskjellen mellom disse to tallene alltid skal være null.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

I tillegg må du overvåke hvordan produsenten har det, om arrangementer-buss mottok meldinger, og hvordan forbrukeren har det. For eksempel, i diagrammene nedenfor, fungerer Refund Tool bra, men BIR har tydeligvis noen problemer (blå topper).

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Jeg har allerede nevnt forbrukergruppeforsinkelse. Grovt sett er dette antallet uleste meldinger. Generelt jobber våre forbrukere raskt, så etterslepet er vanligvis 0, men noen ganger kan det være en kortsiktig topp. Kafka kan gjøre dette rett ut av esken, men du må angi et visst intervall.

Det er et prosjekt Burrowsom vil gi deg mer informasjon om Kafka. Den bruker ganske enkelt forbrukergruppens API for å gi status for hvordan denne gruppen har det. I tillegg til OK og Failed er det en advarsel, og du kan finne ut at dine forbrukere ikke takler produksjonstakten – de har ikke tid til å korrekturlese det som står. Systemet er ganske smart og enkelt å bruke.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

Slik ser API-responsen ut. Her er gruppen bob-live-fifa, partisjon refund.update.v1, status OK, lag 0 - den siste siste offset slik og slik.

Erfaring med å utvikle Refund Tool-tjenesten med en asynkron API på Kafka

overvåking updated_at SLA (fast) Jeg har allerede nevnt. For eksempel har produktet endret til status at det er klart for retur. Vi installerer Cron, som sier at hvis dette objektet ikke har gått tilbake til tilbakebetaling innen 5 minutter (vi returnerer penger gjennom betalingssystemer veldig raskt), så har noe definitivt gått galt, og dette er definitivt en sak for støtte. Derfor tar vi ganske enkelt Cron, som leser slike ting, og hvis de er større enn 0, så sender den et varsel.

For å oppsummere er det praktisk å bruke hendelser når:

  • informasjon er nødvendig av flere systemer;
  • resultatet av behandlingen er ikke viktig;
  • det er få arrangementer eller små arrangementer.

Det ser ut til at artikkelen har et veldig spesifikt emne - asynkron API på Kafka, men i forbindelse med det vil jeg gjerne anbefale mange ting på en gang.
Først, neste Høybelastning++ vi må vente til november, i april kommer det en St. Petersburg-versjon, og i juni skal vi snakke om høy belastning i Novosibirsk.
For det andre er forfatteren av rapporten, Sergei Zaika, medlem av programkomiteen for vår nye konferanse om kunnskapsledelse KnowledgeConf. Konferansen er en dag, vil finne sted 26. april, men programmet er veldig intenst.
Og det blir i mai PHP Russland и RIT++ (med DevOpsConf inkludert) - du kan også foreslå emnet ditt der, snakke om opplevelsen din og klage på de utstoppede kjeglene dine.

Kilde: www.habr.com

Legg til en kommentar