Kuinka Kafkasta tuli todellisuutta

Kuinka Kafkasta tuli todellisuutta

Hei Habr!

Työskentelen Tinkoff-tiimissä, joka kehittää omaa ilmoituskeskustaan. Kehitän enimmäkseen Java-kielellä Spring bootin avulla ja ratkaisen erilaisia ​​projektissa ilmeneviä teknisiä ongelmia.

Suurin osa mikropalveluistamme kommunikoi keskenään asynkronisesti viestivälittäjän kautta. Aikaisemmin käytimme välittäjänä IBM MQ:ta, joka ei enää kestänyt kuormaa, mutta samalla oli korkea toimitustakuu.

Korvaajaksi meille tarjottiin Apache Kafkaa, jolla on korkea skaalauspotentiaali, mutta valitettavasti se vaatii lähes yksilöllistä lähestymistapaa konfigurointiin eri skenaarioissa. Lisäksi Kafkassa oletuksena toimiva vähintään kerran toimitusmekanismi ei mahdollistanut vaaditun johdonmukaisuuden säilyttämistä pakkauksesta. Seuraavaksi jaan kokemuksemme Kafka-konfiguroinnista, erityisesti kerron kuinka konfiguroida ja elää sen kanssa täsmälleen toimituksen jälkeen.

Taattu toimitus ja paljon muuta

Alla käsitellyt asetukset auttavat estämään useita oletusyhteysasetuksiin liittyviä ongelmia. Mutta ensin haluaisin kiinnittää huomiota yhteen parametriin, joka helpottaa mahdollista virheenkorjausta.

Tämä auttaa Asiakastunnus tuottajalle ja kuluttajalle. Ensi silmäyksellä voit käyttää sovelluksen nimeä arvona, ja useimmissa tapauksissa tämä toimii. Vaikka tilanne, jossa sovellus käyttää useita kuluttajia ja annat heille saman client.id:n, johtaa seuraavan varoituksen:

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

Jos haluat käyttää JMX:ää sovelluksessa Kafkan kanssa, tämä voi olla ongelma. Tässä tapauksessa on parasta käyttää client.id-arvona sovelluksen nimen ja esimerkiksi aiheen nimen yhdistelmää. Kokoonpanomme tulos näkyy komennon lähdössä kafka-kuluttajaryhmät Confluentin apuohjelmista:

Kuinka Kafkasta tuli todellisuutta

Katsotaan nyt skenaariota viestin taatusta toimituksesta. Kafka Producerilla on parametri kuittaukset, jonka avulla voit määrittää, kuinka monen kuittauksen jälkeen klusterin johtajan on harkittava viestin onnistuneesti kirjoitettua. Tämä parametri voi saada seuraavat arvot:

  • 0 — kuittausta ei oteta huomioon.
  • 1 on oletusparametri, vain 1 replika vaaditaan kuittaamiseen.
  • −1 — vaaditaan kuittaus kaikista synkronoiduista replikoista (klusterin asennus min.insync.replicas).

Listatuista arvoista on selvää, että kuittaukset, jotka ovat yhtä suuria kuin −1, antavat vahvimman takuun siitä, että viesti ei katoa.

Kuten kaikki tiedämme, hajautetut järjestelmät ovat epäluotettavia. Kafka Producer tarjoaa vaihtoehdon suojatakseen ohimeneviä vikoja vastaan yrittää uudelleen, jonka avulla voit määrittää uudelleenlähetysyritysten määrän sisällä toimitus.aikakatkaisu.ms. Koska uudelleenyritysparametrin oletusarvo on Kokonaisluku.MAX_ARVO (2147483647), viestien uudelleenyritysten määrää voidaan säätää muuttamalla vain toimitus.timeout.ms.

Siirrymme kohti täsmälleen kerran toimitusta

Listattujen asetusten avulla tuottajamme voi toimittaa viestejä korkealla takuulla. Puhutaanpa nyt siitä, kuinka varmistaa, että vain yksi kopio viestistä kirjoitetaan Kafka-aiheeseen? Yksinkertaisimmassa tapauksessa sinun on asetettava parametri Producerissa tätä varten enable.idempotence totta. Idempotenssi takaa, että vain yksi viesti kirjoitetaan yhden aiheen tiettyyn osioon. Idempotenssin mahdollistamisen ehtona ovat arvot acks = kaikki, yritä uudelleen > 0, max.in.flight.requests.per.connection ≤ 5. Jos kehittäjä ei ole määrittänyt näitä parametreja, yllä olevat arvot asetetaan automaattisesti.

Kun idempotenssi on määritetty, on varmistettava, että samat viestit päätyvät aina samoihin osioihin. Tämä voidaan tehdä asettamalla partitioner.class-avaimeksi ja parametriksi Producer. Aloitetaan avaimesta. Sen on oltava sama jokaisessa lähetyksessä. Tämä voidaan saavuttaa helposti käyttämällä mitä tahansa alkuperäisen viestin yritystunnuksia. Partitioner.class-parametrilla on oletusarvo − DefaultPartitioner. Tällä osiointistrategialla toimimme oletusarvoisesti seuraavasti:

  • Jos osio on erikseen määritetty viestiä lähetettäessä, käytämme sitä.
  • Jos osiota ei ole määritetty, mutta avain on määritetty, valitse osio avaimen hajautusarvon perusteella.
  • Jos osiota ja avainta ei ole määritetty, valitse osiot yksitellen (kierto-robin).

Myös avaimen käyttö ja idempotentti lähettäminen parametrin kanssa max.in.flight.requests.per.connection = 1 antaa sinulle virtaviivaistetun viestien käsittelyn kuluttajalle. On myös syytä muistaa, että jos pääsynhallinta on määritetty klusteriisi, tarvitset oikeudet kirjoittaaksesi aiheeseen.

Jos sinulta puuttuu yhtäkkiä idempotenttilähetys avaimella tai Producer-puolen logiikka vaatii tietojen johdonmukaisuuden ylläpitämistä eri osioiden välillä, tapahtumat tulevat apuun. Lisäksi ketjutapahtuman avulla voit ehdollisesti synkronoida esimerkiksi Kafkan tietueen tietokannan tietueen kanssa. Jotta tapahtuman lähettäminen Tuottajalle olisi mahdollista, sen on oltava idempotentti ja lisäksi asetettu transaktio.id. Jos Kafka-klusteriisi on määritetty pääsynhallinta, tapahtumatietue, kuten idempotentti tietue, tarvitsee kirjoitusoikeudet, jotka voidaan myöntää maskilla käyttämällä transaktional.id-tiedostoon tallennettua arvoa.

Muodollisesti mitä tahansa merkkijonoa, kuten sovelluksen nimeä, voidaan käyttää tapahtuman tunnisteena. Mutta jos käynnistät useita saman sovelluksen esiintymiä samalla transaktional.id:llä, ensimmäinen käynnistetty ilmentymä pysäytetään virheen vuoksi, koska Kafka pitää sitä zombiprosessina.

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.

Tämän ongelman ratkaisemiseksi lisäämme sovelluksen nimeen loppuliitteen isäntänimen muodossa, jonka saamme ympäristömuuttujista.

Tuottaja on määritetty, mutta Kafkan tapahtumat hallitsevat vain viestin laajuutta. Tapahtuman tilasta riippumatta viesti siirtyy välittömästi aiheeseen, mutta siinä on ylimääräisiä järjestelmämääritteitä.

Jotta kuluttaja ei voi lukea tällaisia ​​viestejä etukäteen, sen on asetettava parametri isolation.level arvoon read_committed. Tällainen Kuluttaja voi lukea ei-tapahtumaan liittyviä viestejä kuten ennen ja tapahtumaviestejä vasta sitoutumisen jälkeen.
Jos olet määrittänyt kaikki aiemmin luetellut asetukset, olet määrittänyt täsmälleen kerran toimituksen. Onnittelut!

Mutta on vielä yksi vivahde. Transaction.id, jonka määritimme edellä, on itse asiassa tapahtuman etuliite. Tapahtumanhallinnassa siihen lisätään järjestysnumero. Vastaanotettu tunniste myönnetään vastaanottajalle transaktional.id.expiration.ms, joka on määritetty Kafka-klusteriin ja jonka oletusarvo on "7 päivää". Jos sovellus ei ole tänä aikana vastaanottanut yhtään viestiä, saat kun yrität seuraavaa tapahtumalähetystä InvalidPidMappingException. Tapahtumakoordinaattori antaa sitten uuden järjestysnumeron seuraavalle tapahtumalle. Viesti voi kuitenkin kadota, jos InvalidPidMappingException -asetusta ei käsitellä oikein.

Yhteensä

Kuten näette, pelkkä viestien lähettäminen Kafkalle ei riitä. Sinun on valittava parametrien yhdistelmä ja oltava valmis tekemään nopeita muutoksia. Tässä artikkelissa yritin näyttää yksityiskohtaisesti täsmälleen kerran toimituksen asetukset ja kuvailin useita kohtaamiamme ongelmia client.id- ja transaktional.id-kokoonpanoissa. Alla on yhteenveto tuottajan ja kuluttajan asetuksista.

Tuottaja:

  1. kiitokset = kaikki
  2. yrittää uudelleen > 0
  3. enable.idempotence = tosi
  4. max.in.flight.requests.per.connection ≤ 5 (1 säännölliseen lähetykseen)
  5. transaktional.id = ${sovelluksen_nimi}-${isäntänimi}

Kuluttaja:

  1. isolation.level = read_committed

Virheiden minimoimiseksi tulevissa sovelluksissa teimme oman kääreemme jousikokoonpanon päälle, jossa joidenkin lueteltujen parametrien arvot on jo asetettu.

Tässä pari materiaalia itseopiskeluun:

Lähde: will.com

Lisää kommentti