Ako sa Kafka stal realitou

Ako sa Kafka stal realitou

Čau Habr!

Pracujem v tíme Tinkoff, ktorý vyvíja vlastné notifikačné centrum. Väčšinou vyvíjam v Jave pomocou Spring boot a riešim rôzne technické problémy, ktoré vznikajú v projekte.

Väčšina našich mikroslužieb medzi sebou komunikuje asynchrónne prostredníctvom sprostredkovateľa správ. Predtým sme ako brokera využívali IBM MQ, ktorý už nezvládal záťaž, no zároveň mal vysoké garancie doručenia.

Ako náhrada nám bol ponúknutý Apache Kafka, ktorý má vysoký škálovací potenciál, ale bohužiaľ vyžaduje takmer individuálny prístup ku konfigurácii pre rôzne scenáre. Okrem toho aspoň jeden mechanizmus doručovania, ktorý štandardne funguje v Kafke, neumožňoval udržať požadovanú úroveň konzistencie hneď po vybalení. Ďalej sa podelím o naše skúsenosti s konfiguráciou Kafka, najmä vám poviem, ako nakonfigurovať a žiť s presne jednou dodávkou.

Garantované doručenie a ďalšie

Nastavenia uvedené nižšie pomôžu predísť mnohým problémom s predvolenými nastaveniami pripojenia. Najprv by som však chcel venovať pozornosť jednému parametru, ktorý uľahčí prípadné ladenie.

Toto pomôže client.id pre výrobcu a spotrebiteľa. Na prvý pohľad môžete ako hodnotu použiť názov aplikácie a vo väčšine prípadov to bude fungovať. Hoci situácia, keď aplikácia používa niekoľko spotrebiteľov a vy im dáte rovnaké client.id, vedie k nasledujúcemu varovaniu:

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

Ak chcete použiť JMX v aplikácii s Kafkou, môže to byť problém. Pre tento prípad je najlepšie použiť ako hodnotu client.id kombináciu názvu aplikácie a napríklad názvu témy. Výsledok našej konfigurácie je možné vidieť vo výstupe príkazu kafka-spotrebiteľské-skupiny z verejných služieb od spoločnosti Confluence:

Ako sa Kafka stal realitou

Teraz sa pozrime na scenár garantovaného doručenia správy. Kafka Producer má parameter acks, ktorá vám umožňuje nakonfigurovať, po koľkých potvrdeniach musí vedúci klastra považovať správu za úspešne napísanú. Tento parameter môže nadobúdať nasledujúce hodnoty:

  • 0 – potvrdenie nebude brané do úvahy.
  • 1 je predvolený parameter, na potvrdenie je potrebná iba 1 replika.
  • −1 — vyžaduje sa potvrdenie zo všetkých synchronizovaných replík (nastavenie klastra min.insync.repliky).

Z uvedených hodnôt je zrejmé, že počet odpovedí rovný −1 dáva najsilnejšiu záruku, že sa správa nestratí.

Ako všetci vieme, distribuované systémy sú nespoľahlivé. Na ochranu pred prechodnými poruchami ponúka Kafka Producer možnosť opakuje, ktorá vám umožňuje nastaviť počet pokusov o opätovné odoslanie v rámci delivery.timeout.ms. Keďže parameter opakovaní má predvolenú hodnotu Integer.MAX_VALUE (2147483647), počet opakovaní správy je možné upraviť zmenou iba delivery.timeout.ms.

Smerujeme k presne jednorazovému doručeniu

Uvedené nastavenia umožňujú nášmu Producentovi doručovať správy s vysokou zárukou. Poďme si teraz povedať, ako zabezpečiť, aby sa ku kafkovej téme napísala iba jedna kópia správy? V najjednoduchšom prípade, aby ste to urobili, musíte nastaviť parameter na Producer umožniť.idempotencia na pravdu. Idempotency zaručuje, že do konkrétneho oddielu jednej témy sa zapíše iba jedna správa. Predpokladom umožnenia idempotencie sú hodnoty acks = všetko, zopakovať > 0, max.požiadaviek počas letu na pripojenie ≤ 5. Ak tieto parametre nešpecifikuje vývojár, vyššie uvedené hodnoty sa nastavia automaticky.

Keď je nakonfigurovaná idempotencia, je potrebné zabezpečiť, aby rovnaké správy vždy skončili v rovnakých oddieloch. Môžete to urobiť nastavením kľúča a parametra partitioner.class na Producer. Začnime kľúčom. Musí byť rovnaký pre každé predloženie. To sa dá ľahko dosiahnuť použitím ktoréhokoľvek obchodného ID z pôvodného príspevku. Parameter partitioner.class má predvolenú hodnotu − DefaultPartitioner. S touto stratégiou rozdelenia sa štandardne správame takto:

  • Ak je oddiel pri odosielaní správy explicitne špecifikovaný, použijeme ho.
  • Ak oddiel nie je zadaný, ale je zadaný kľúč, vyberte oddiel podľa hash kľúča.
  • Ak nie je zadaný oddiel a kľúč, vyberte oddiely jeden po druhom (cyklus).

Tiež pomocou kľúča a idempotentného odosielania s parametrom maximálny počet žiadostí počas letu na pripojenie = 1 vám poskytuje zjednodušené spracovanie správ na spotrebiteľovi. Je tiež potrebné pripomenúť, že ak je vo vašom klastri nakonfigurované riadenie prístupu, budete potrebovať práva na idempotentný zápis do témy.

Ak vám zrazu chýbajú možnosti idempotentného odosielania kľúčom alebo logika na strane výrobcu vyžaduje zachovanie konzistencie údajov medzi rôznymi oddielmi, na záchranu prídu transakcie. Navyše pomocou reťazovej transakcie môžete podmienečne synchronizovať záznam v Kafke napríklad so záznamom v databáze. Aby bolo možné transakčné odosielanie výrobcovi, musí byť idempotentné a dodatočne nastavené transakčné.id. Ak má váš klaster Kafka nakonfigurované riadenie prístupu, transakčný záznam, podobne ako idempotentný záznam, bude potrebovať oprávnenia na zápis, ktoré možno udeliť maskou pomocou hodnoty uloženej v transakčnom.id.

Formálne môže byť ako identifikátor transakcie použitý akýkoľvek reťazec, napríklad názov aplikácie. Ak však spustíte niekoľko inštancií tej istej aplikácie s rovnakým transakčným.id, prvá spustená inštancia sa zastaví s chybou, pretože Kafka to bude považovať za zombie proces.

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.

Aby sme tento problém vyriešili, pridáme k názvu aplikácie príponu v podobe názvu hostiteľa, ktorý získame z premenných prostredia.

Producent je nakonfigurovaný, ale transakcie na Kafke kontrolujú iba rozsah správy. Bez ohľadu na stav transakcie správa okamžite prejde k téme, ale má ďalšie systémové atribúty.

Aby spotrebiteľ takéto správy nečítal vopred, musí nastaviť parameter izolácia.úroveň na hodnotu read_committed. Takýto spotrebiteľ bude môcť čítať netransakčné správy ako predtým a transakčné správy až po potvrdení.
Ak ste nastavili všetky vyššie uvedené nastavenia, konfigurovali ste presne po dodaní. Gratulujem!

Ale je tu ešte jedna nuansa. Transactional.id, ktorý sme nakonfigurovali vyššie, je v skutočnosti predponou transakcie. V správcovi transakcií sa k nemu pridá poradové číslo. Prijatý identifikátor je vydaný na transakčné.id.vypršanie platnosti.ms, ktorý je nakonfigurovaný na klastri Kafka a má predvolenú hodnotu „7 dní“. Ak počas tejto doby aplikácia neprijala žiadne správy, potom pri pokuse o ďalšie odoslanie transakcie dostanete InvalidPidMappingException. Koordinátor transakcie potom vydá nové poradové číslo pre ďalšiu transakciu. Správa sa však môže stratiť, ak sa výnimka InvalidPidMappingException nespracuje správne.

Namiesto výsledkov

Ako vidíte, nestačí len posielať správy Kafkovi. Musíte si vybrať kombináciu parametrov a byť pripravený na rýchle zmeny. V tomto článku som sa pokúsil podrobne ukázať nastavenie presne raz doručenia a opísal som niekoľko problémov s konfiguráciami client.id a transactional.id, s ktorými sme sa stretli. Nižšie je uvedený súhrn nastavení výrobcu a spotrebiteľa.

Výrobca:

  1. acks = všetko
  2. pokusy > 0
  3. umožniť.idempotencia = pravda
  4. max.požiadavky.za letu.na pripojenie ≤ 5 (1 pre riadne odoslanie)
  5. transakcia.id = ${názov-aplikácie}-${názov hostiteľa}

Spotrebiteľ:

  1. isolation.level = read_committed

Aby sme minimalizovali chyby v budúcich aplikáciách, vytvorili sme vlastný wrapper nad konfiguráciou pružiny, kde sú už nastavené hodnoty pre niektoré z uvedených parametrov.

Tu je pár materiálov pre samoštúdium:

Zdroj: hab.com

Pridať komentár