Hvordan Kafka ble virkelighet

Hvordan Kafka ble virkelighet

Hei Habr!

Jeg jobber på Tinkoff-teamet, som utvikler sitt eget varslingssenter. Jeg utvikler for det meste i Java ved hjelp av Spring boot og løser ulike tekniske problemer som oppstår i et prosjekt.

De fleste av våre mikrotjenester kommuniserer med hverandre asynkront gjennom en meldingsmegler. Tidligere brukte vi IBM MQ som megler, som ikke lenger kunne takle belastningen, men samtidig hadde høye leveringsgarantier.

Som erstatning ble vi tilbudt Apache Kafka, som har høyt skaleringspotensial, men som dessverre krever en nesten individuell tilnærming til konfigurasjon for ulike scenarier. I tillegg tillot ikke leveringsmekanismen minst én gang som fungerer i Kafka som standard å opprettholde det nødvendige nivået av konsistens ut av esken. Deretter vil jeg dele vår erfaring med Kafka-konfigurasjon, spesielt vil jeg fortelle deg hvordan du konfigurerer og lever med nøyaktig en gang levering.

Garantert levering og mer

Innstillingene diskutert nedenfor vil bidra til å forhindre en rekke problemer med standard tilkoblingsinnstillinger. Men først vil jeg ta hensyn til en parameter som vil lette en mulig feilsøking.

Dette vil hjelpe klient-ID for produsent og forbruker. Ved første øyekast kan du bruke applikasjonsnavnet som verdi, og i de fleste tilfeller vil dette fungere. Selv om situasjonen når en applikasjon bruker flere forbrukere og du gir dem samme client.id, resulterer i følgende advarsel:

org.apache.kafka.common.utils.AppInfoParser — Error registering AppInfo mbean javax.management.InstanceAlreadyExistsException: kafka.consumer:type=app-info,id=kafka.test-0

Hvis du vil bruke JMX i en applikasjon med Kafka, kan dette være et problem. I dette tilfellet er det best å bruke en kombinasjon av applikasjonsnavnet og for eksempel emnenavnet som client.id-verdi. Resultatet av konfigurasjonen vår kan sees i kommandoutgangen kafka-forbrukergrupper fra verktøy fra Confluent:

Hvordan Kafka ble virkelighet

La oss nå se på scenariet for garantert meldingslevering. Kafka Producer har en parameter akks, som lar deg konfigurere etter hvor mange bekreftelser klyngelederen trenger for å vurdere meldingen som vellykket skrevet. Denne parameteren kan ha følgende verdier:

  • 0 — anerkjenne vil ikke bli vurdert.
  • 1 er standardparameteren, bare 1 replika kreves for å bekrefte.
  • −1 — bekreftelse fra alle synkroniserte replikaer kreves (klyngeoppsett min.insync.replicas).

Fra de oppførte verdiene er det klart at acks lik -1 gir den sterkeste garantien for at meldingen ikke går tapt.

Som vi alle vet, er distribuerte systemer upålitelige. For å beskytte mot forbigående feil, tilbyr Kafka Producer alternativet prøver på nytt, som lar deg angi antall forsøk på å sende på nytt innen leveringstidsavbrudd.ms. Siden gjenforsøk-parameteren har en standardverdi på Integer.MAX_VALUE (2147483647), kan antall gjenforsøk på meldinger justeres ved å endre levering.timeout.ms.

Vi går mot nøyaktig en gang levering

De oppførte innstillingene lar produsenten vår levere meldinger med høy garanti. La oss nå snakke om hvordan vi sikrer at bare én kopi av en melding er skrevet til et Kafka-emne? I det enkleste tilfellet, for å gjøre dette, må du sette parameteren på Producer aktivere.idempotens til sant. Idempotens garanterer at kun én melding skrives til en bestemt partisjon av ett emne. Forutsetningen for å muliggjøre idempotens er verdiene acks = alle, prøv på nytt > 0, maks.in.flight.requests.per.connection ≤ 5. Hvis disse parameterne ikke er spesifisert av utvikleren, vil verdiene ovenfor automatisk settes.

Når idempotens er konfigurert, er det nødvendig å sikre at de samme meldingene havner i de samme partisjonene hver gang. Dette kan gjøres ved å sette partitioner.class-nøkkelen og parameteren til Producer. La oss starte med nøkkelen. Det må være likt for hver innlevering. Dette kan enkelt oppnås ved å bruke hvilken som helst av forretnings-ID-ene fra det opprinnelige innlegget. Parameteren partitioner.class har en standardverdi − DefaultPartitioner. Med denne partisjoneringsstrategien fungerer vi som standard slik:

  • Hvis partisjonen er eksplisitt spesifisert når du sender meldingen, bruker vi den.
  • Hvis partisjonen ikke er spesifisert, men nøkkelen er spesifisert, velg partisjonen ved hjelp av hashen til nøkkelen.
  • Hvis partisjonen og nøkkelen ikke er spesifisert, velg partisjonene én etter én (round-robin).

Også bruk av en nøkkel og idempotent sending med en parameter max.in.flight.requests.per.connection = 1 gir deg strømlinjeformet meldingsbehandling på Consumer. Det er også verdt å huske at hvis tilgangskontroll er konfigurert på klyngen din, vil du trenge rettigheter til å idempotent skrive til et emne.

Hvis du plutselig mangler evnene til idempotent sending med nøkkel eller logikken på produsentsiden krever å opprettholde datakonsistens mellom forskjellige partisjoner, vil transaksjoner komme til unnsetning. I tillegg, ved å bruke en kjedetransaksjon, kan du betinget synkronisere en post i Kafka, for eksempel med en post i databasen. For å muliggjøre transaksjonssending til produsenten, må den være idempotent og i tillegg innstilt transaksjons-id. Hvis Kafka-klyngen din har tilgangskontroll konfigurert, vil en transaksjonspost, som en idempotent post, trenge skrivetillatelser, som kan gis med maske ved å bruke verdien lagret i transactional.id.

Formelt sett kan en hvilken som helst streng, for eksempel applikasjonsnavnet, brukes som en transaksjonsidentifikator. Men hvis du starter flere forekomster av samme applikasjon med samme transactional.id, vil den første lanserte forekomsten bli stoppet med en feil, siden Kafka vil betrakte det som en zombieprosess.

org.apache.kafka.common.errors.ProducerFencedException: Producer attempted an operation with an old epoch. Either there is a newer producer with the same transactionalId, or the producer's transaction has been expired by the broker.

For å løse dette problemet legger vi til et suffiks til applikasjonsnavnet i form av vertsnavnet, som vi henter fra miljøvariabler.

Produsenten er konfigurert, men transaksjoner på Kafka kontrollerer bare omfanget av meldingen. Uavhengig av transaksjonsstatus, går meldingen umiddelbart til emnet, men har flere systemattributter.

For å forhindre at slike meldinger blir lest av forbrukeren på forhånd, må den angi parameteren isolasjonsnivå til read_committed verdi. En slik forbruker vil være i stand til å lese ikke-transaksjonelle meldinger som før, og transaksjonsmeldinger kun etter en commit.
Hvis du har angitt alle innstillingene som er oppført tidligere, har du konfigurert nøyaktig en gang levering. Gratulerer!

Men det er en nyanse til. Transactional.id, som vi konfigurerte ovenfor, er faktisk transaksjonsprefikset. På transaksjonsbehandleren legges det til et sekvensnummer. Den mottatte identifikatoren utstedes til transaksjons-id.utløp.ms, som er konfigurert på en Kafka-klynge og har en standardverdi på "7 dager". Hvis applikasjonen i løpet av denne tiden ikke har mottatt noen meldinger, vil du motta den neste transaksjonssendingen når du prøver InvalidPidMappingException. Transaksjonskoordinator vil da utstede et nytt sekvensnummer for neste transaksjon. Meldingen kan imidlertid gå tapt hvis InvalidPidMappingException ikke håndteres på riktig måte.

I stedet for totals

Som du kan se, er det ikke nok å bare sende meldinger til Kafka. Du må velge en kombinasjon av parametere og være forberedt på å gjøre raske endringer. I denne artikkelen prøvde jeg å vise i detalj oppsettet for nøyaktig levering én gang og beskrev flere problemer med client.id og transactional.id-konfigurasjonene som vi møtte. Nedenfor er et sammendrag av produsent- og forbrukerinnstillingene.

Produsent:

  1. acks = alle
  2. prøver på nytt > 0
  3. enable.idempotence = sant
  4. max.in.flight.requests.per.connection ≤ 5 (1 for ryddig sending)
  5. transactional.id = ${application-name}-${vertsnavn}

Forbruker:

  1. isolation.level = read_committed

For å minimere feil i fremtidige applikasjoner, laget vi vår egen omslag over fjærkonfigurasjonen, der verdiene for noen av de listede parameterne allerede er satt.

Her er et par materialer for selvstudier:

Kilde: www.habr.com

Legg til en kommentar