Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 3. fejezet Kafka

Egy kis könyv fordításának folytatása:
Az üzenetközvetítők megértése
szerző: Jakub Korab, kiadó: O'Reilly Media, Inc., megjelenés dátuma: 2017. június, ISBN: 9781492049296.

Előző lefordított rész: Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 1. fejezet Bevezetés

3. FEJEZET

Kafka

A Kafka-t a LinkedIn fejlesztette ki, hogy megkerülje a hagyományos üzenetközvetítők korlátait, és elkerülje, hogy több üzenetközvetítőt kelljen beállítani a különböző pont-pont interakciókhoz. Ennek leírása ebben a könyvben a „Megnagyobbítás” részben, 28. oldalon található. Használati esetek A LinkedIn nagyrészt nagyon nagy mennyiségű adat, például oldalkattintás és hozzáférési napló egyirányú feldolgozásán támaszkodott, miközben továbbra is lehetővé tette, hogy ezeket az adatokat több rendszer is felhasználja anélkül, hogy ez befolyásolná a gyártók vagy más fogyasztók termelékenységét. Valójában a Kafka létezésének oka az, hogy olyan üzenetkezelési architektúrát kapjon, amelyet az Universal Data Pipeline ír le.

Ezt a végső célt tekintve természetesen más követelmények is felmerültek. Kafkának:

  • Legyen rendkívül gyors
  • Nagyobb sávszélesség biztosítása üzenetekkel való munka során
  • Támogassa a kiadó-előfizető és a pont-pont modelleket
  • Ne lassítson a fogyasztók hozzáadásával. Például az ActiveMQ-ban mind a várólista, mind a témakör teljesítménye csökken, ahogy a célhelyen lévő fogyasztók száma növekszik.
  • Legyen vízszintesen méretezhető; ha egy közvetítő, amely továbbra is üzeneteket küld, ezt csak maximális lemezsebességgel tudja megtenni, akkor a teljesítmény növelése érdekében érdemes túllépni egyetlen közvetítő példányon.
  • Korlátozza a hozzáférést az üzenetek tárolására és újbóli lekérésére

Mindezek elérése érdekében a Kafka olyan architektúrát alkalmazott, amely újradefiniálta az ügyfelek és az üzenetküldő brókerek szerepét és felelősségét. A JMS modell nagyon bróker-orientált, ahol a bróker felelős az üzenetek terjesztéséért, az ügyfeleknek pedig csak az üzenetek küldésével és fogadásával kell foglalkozniuk. A Kafka ezzel szemben ügyfélközpontú, az ügyfél a hagyományos bróker számos funkcióját átveszi, például a releváns üzenetek méltányos elosztását a fogyasztók felé, cserébe egy rendkívül gyors és skálázható brókerért. Azok számára, akik hagyományos üzenetküldő rendszerekkel dolgoztak, a Kafkával való munka alapvető gondolkodásmódot igényel.
Ez a mérnöki irány egy olyan üzenetküldő infrastruktúra létrehozásához vezetett, amely a hagyományos brókerhez képest sok nagyságrenddel képes növelni az átviteli sebességet. Amint látni fogjuk, ez a megközelítés kompromisszumokkal jár, ami azt jelenti, hogy a Kafka nem alkalmas bizonyos típusú terhelésekhez és telepített szoftverekhez.

Egységes célmodell

A fent leírt követelmények teljesítése érdekében a Kafka a közzétételi-előfizetési és a pont-pont közötti üzenetküldést egyféle célállomáson kombinálta. téma. Ez zavaró azoknak, akik üzenetküldő rendszerekkel dolgoztak, ahol a "téma" szó olyan közvetítési mechanizmusra utal, amelyből (a témából) az olvasás nem tartós. A Kafka-témákat a könyv bevezetőjében meghatározott hibrid desztinációtípusnak kell tekinteni.

A fejezet hátralévő részében, hacsak kifejezetten másként nem jelezzük, a "téma" kifejezés egy Kafka-témára vonatkozik.

Ahhoz, hogy teljes mértékben megértsük, hogyan viselkednek a témák, és milyen garanciákat nyújtanak, először meg kell vizsgálnunk, hogyan valósítják meg őket a Kafkában.
A Kafka minden témája saját naplóval rendelkezik.
A Kafkának üzenetet küldő gyártók ebbe a naplóba írnak, a fogyasztók pedig folyamatosan előremutató mutatók segítségével olvasnak a naplóból. Kafka időnként törli a napló legrégebbi részeit, függetlenül attól, hogy az ezekben a részekben lévő üzeneteket elolvasták-e vagy sem. A Kafka tervezésének központi része, hogy a brókert nem érdekli, hogy elolvassák-e az üzeneteket vagy sem – ez az ügyfél felelőssége.

A "napló" és a "mutató" kifejezések nem jelennek meg Kafka dokumentáció. Ezeket a jól ismert kifejezéseket használjuk itt, hogy segítsék a megértést.

Ez a modell teljesen eltér az ActiveMQ-tól, ahol az összes sor üzenetei ugyanabban a naplóban vannak tárolva, és a közvetítő az üzeneteket az olvasás után töröltként jelöli meg.
Most ássunk egy kicsit mélyebbre, és nézzük meg részletesebben a témanaplót.
A Kafka napló több partícióból áll (Ábra 3 1-). A Kafka garantálja a szigorú sorrendet minden partícióban. Ez azt jelenti, hogy a partícióra meghatározott sorrendben írt üzenetek ugyanabban a sorrendben kerülnek beolvasásra. Minden partíció gördülő naplófájlként valósul meg, amely tartalmazza részhalmaz (részhalmaza) a témához a producerei által küldött összes üzenetből. A létrehozott témakör alapértelmezés szerint egy partíciót tartalmaz. A partíciók ötlete Kafka központi ötlete a vízszintes méretezéshez.

Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 3. fejezet Kafka
3-1. ábra. Kafka válaszfalak

Amikor egy producer üzenetet küld egy Kafka-témához, eldönti, hogy melyik partícióra küldje az üzenetet. Ezt a későbbiekben részletesebben is megvizsgáljuk.

Üzenetek olvasása

Az üzeneteket olvasni kívánó kliens egy nevesített mutatót kezel fogyasztói csoport, ami arra mutat beszámítás üzeneteket a partícióban. Az eltolás egy növekményes pozíció, amely 0-tól kezdődik a partíció elején. Ez a fogyasztói csoport, amelyre az API-ban a felhasználó által megadott csoportazonosítón keresztül hivatkozik, megfelel a egy logikus fogyasztó vagy rendszer.

A legtöbb üzenetkezelő rendszer több példány és szál használatával olvassa be a célállomás adatait, hogy párhuzamosan dolgozza fel az üzeneteket. Így általában sok fogyasztói példány osztozik ugyanazon a fogyasztói csoporton.

Az olvasás problémája a következőképpen ábrázolható:

  • A téma több partícióval rendelkezik
  • Egy témát egyszerre több fogyasztói csoport is használhat
  • A fogyasztók egy csoportjának több különálló példánya is lehet

Ez egy nem triviális sok a sokhoz probléma. Annak megértéséhez, hogy Kafka hogyan kezeli a fogyasztói csoportok, fogyasztói példányok és partíciók közötti kapcsolatokat, nézzünk meg egy sor fokozatosan összetettebb olvasási forgatókönyvet.

Fogyasztók és fogyasztói csoportok

Vegyünk kiindulópontnak egy témát egy partícióval (Ábra 3 2-).

Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 3. fejezet Kafka
3-2. ábra. A fogyasztó partícióról olvas

Amikor egy fogyasztói példány a saját csoportazonosítójával csatlakozik ehhez a témához, a rendszer hozzárendel egy olvasási partíciót és egy eltolást ezen a partíción. Az eltolás pozíciója a kliensben a legutóbbi pozícióra (legújabb üzenet) vagy a legkorábbi pozícióra (legrégebbi üzenet) mutató mutatóként van konfigurálva. A fogyasztó üzeneteket kér (lekérdez) a témából, aminek hatására azok sorra kerülnek kiolvasásra a naplóból.
Az offset pozíciót rendszeresen visszakötjük Kafkához, és üzenetként tároljuk egy belső témában _consumer_offsets. Az olvasott üzenetek továbbra sem törlődnek, ellentétben a hagyományos közvetítőkkel, és az ügyfél visszatekerheti az eltolást, hogy újra feldolgozza a már megtekintett üzeneteket.

Amikor egy második logikai fogyasztó egy másik group_id használatával csatlakozik, egy második mutatót kezel, amely független az elsőtől (Ábra 3 3-). Így egy Kafka-téma úgy működik, mint egy sor, ahol egy fogyasztó van, és mint egy normál közzététel-feliratkozás (pub-sub) téma, amelyre több fogyasztó is feliratkozik, azzal a további előnnyel, hogy az összes üzenetet tárolja és többször is feldolgozhatja.

Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 3. fejezet Kafka
3-3 ábra. Két különböző fogyasztói csoportba tartozó fogyasztó olvas ugyanabból a partícióból

Fogyasztók egy fogyasztói csoportban

Amikor egy fogyasztói példány adatokat olvas egy partícióról, teljes mértékben uralja a mutatót, és az előző részben leírtak szerint dolgozza fel az üzeneteket.
Ha több fogyasztó ugyanazzal a group_id-vel csatlakozott egy témához egy partícióval, akkor az utoljára csatlakozott példány kap irányítást a mutató felett, és ettől a pillanattól kezdve megkapja az összes üzenetet (Ábra 3 4-).

Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 3. fejezet Kafka
3-4. Ugyanabban a fogyasztói csoportban két fogyasztó olvas ugyanabból a partícióból

Ez a feldolgozási mód, amelyben a fogyasztói példányok száma meghaladja a partíciók számát, egyfajta kizárólagos fogyasztóként fogható fel. Ez akkor lehet hasznos, ha a fogyasztói példányok "aktív-passzív" (vagy "meleg-meleg") klaszterezésre van szüksége, bár több fogyasztó párhuzamos futtatása ("aktív-aktív" vagy "forró-meleg") sokkal jellemzőbb, mint készenléti állapotban.

Ez a fent leírt üzenetelosztási viselkedés meglepő lehet egy normál JMS-sor viselkedéséhez képest. Ebben a modellben a sorba küldött üzenetek egyenletesen oszlanak el a két fogyasztó között.

Leggyakrabban, amikor több fogyasztói példányt hozunk létre, ezt vagy az üzenetek párhuzamos feldolgozására, vagy az olvasási sebesség növelésére, vagy az olvasási folyamat stabilitásának növelésére tesszük. Mivel egyszerre csak egy fogyasztói példány tud adatokat olvasni egy partícióról, hogyan érhető el ez a Kafkában?

Ennek egyik módja az, hogy egyetlen fogyasztói példányt használva az összes üzenet elolvasására és a szálkészletnek való továbbítására. Noha ez a megközelítés növeli a feldolgozási sebességet, növeli a fogyasztói logika összetettségét, és semmit sem javít az olvasórendszer robusztusságán. Ha a fogyasztó egyik példánya áramszünet vagy hasonló esemény miatt lemerül, akkor a kivonás leáll.

A kafkai probléma kanonikus megoldása a bОtöbb partíció.

Partícionálás

A partíciók jelentik a fő mechanizmust a témakörök olvasásának és skálázásának párhuzamosítására az egyetlen közvetítőpéldány sávszélességén túl. Ennek jobb megértése érdekében nézzünk meg egy olyan helyzetet, amikor van egy téma két partícióval, és egy fogyasztó feliratkozik erre a témára (Ábra 3 5-).

Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 3. fejezet Kafka
3-5. Egy fogyasztó több partícióból olvas

Ebben a forgatókönyvben a fogyasztó mindkét partícióban megkapja az irányítást a csoportazonosítójának megfelelő mutatók felett, és elkezdi olvasni az üzeneteket mindkét partícióról.
Amikor egy további fogyasztót adnak ehhez a témához ugyanahhoz a csoportazonosítóhoz, Kafka átcsoportosítja az egyik partíciót az elsőről a második fogyasztóra. Ezt követően a fogyasztó minden egyes példánya a téma egy partíciójából olvas (Ábra 3 6-).

Ahhoz, hogy az üzenetek 20 szálban párhuzamosan dolgozzanak fel, legalább 20 partícióra van szükség. Ha kevesebb partíció van, akkor olyan fogyasztók maradnak, akiken nincs mit dolgozni, amint azt korábban az exkluzív fogyasztókról szóló vitában leírtuk.

Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 3. fejezet Kafka
3-6. Ugyanabban a fogyasztói csoportban két fogyasztó különböző partíciókról olvas

Ez a séma nagymértékben csökkenti a Kafka-bróker bonyolultságát a JMS-sor karbantartásához szükséges üzenetelosztáshoz képest. Itt nem kell aggódnia a következő pontok miatt:

  • Melyik fogyasztó kapja meg a következő üzenetet a kör-robin allokáció, az előzetes letöltési pufferek jelenlegi kapacitása vagy a korábbi üzenetek alapján (mint a JMS üzenetcsoportok esetében).
  • Milyen üzeneteket küldenek melyik fogyasztónak, és meghibásodás esetén kell-e azokat újra kézbesíteni.

A Kafka brókernek mindössze annyit kell tennie, hogy egymás után továbbítja az üzeneteket a fogyasztónak, amikor az utóbbi kéri.

A lektorálás és a sikertelen üzenetek újraküldésének párhuzamos követelményei azonban nem szűnnek meg – az ezekért való felelősség egyszerűen átszáll a brókerről az ügyfélre. Ez azt jelenti, hogy ezeket figyelembe kell venni a kódban.

Üzenetek küldése

Az üzenet készítőjének felelőssége eldönteni, hogy melyik partícióra küldjön üzenetet. Ahhoz, hogy megértsük a mechanizmust, amellyel ez megtörténik, először át kell gondolnunk, hogy valójában mit is küldünk.

Míg a JMS-ben metaadatokkal (fejlécekkel és tulajdonságokkal) és a hasznos terhet tartalmazó törzstel (payload) rendelkező üzenetszerkezetet használunk, addig a Kafkában az üzenet "kulcs-érték" pár. Az üzenet hasznos tartalma értékként kerül elküldésre. A kulcsot viszont főleg particionálásra használják, és tartalmaznia kell üzleti logika specifikus kulcshogy a kapcsolódó üzenetek ugyanabba a partícióba kerüljenek.

A 2. fejezetben az online fogadási forgatókönyvet tárgyaltuk, amikor a kapcsolódó eseményeket egyetlen fogyasztónak kell sorrendben feldolgoznia:

  1. A felhasználói fiók konfigurálva van.
  2. A pénz jóváírásra kerül a számlán.
  3. Olyan fogadást kötnek, amely pénzt von le a számláról.

Ha minden esemény egy témához küldött üzenet, akkor a természetes kulcs a fiókazonosító lesz.
Amikor egy üzenetet a Kafka Producer API-val küldenek, az átkerül egy partíciófüggvényhez, amely az üzenet és a Kafka-fürt aktuális állapota alapján visszaadja annak a partíciónak az azonosítóját, amelyre az üzenetet el kell küldeni. Ez a szolgáltatás Java nyelven a Partitioner felületen keresztül valósul meg.

Ez a felület így néz ki:

interface Partitioner {
    int partition(String topic,
        Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);
}

A Partitioner implementáció az alapértelmezett általános célú kivonatoló algoritmust használja a kulcson a partíció meghatározásához, vagy körvizsgálatot, ha nincs megadva kulcs. Ez az alapértelmezett érték a legtöbb esetben jól működik. A jövőben azonban meg akarja majd írni a sajátját.

Saját particionálási stratégia megírása

Nézzünk egy példát, ahol metaadatokat szeretne küldeni az üzenet rakományával együtt. Példánkban a hasznos teher egy utasítás a játékszámlára történő befizetésre. Az utasítás olyan dolog, amelyet garantálni szeretnénk, hogy az átvitel során nem módosuljon, és biztosak akarunk lenni abban, hogy csak egy megbízható upstream rendszer tudja ezt az utasítást elindítani. Ebben az esetben a küldő és fogadó rendszer megállapodik az üzenet hitelesítésére szolgáló aláírás használatáról.
A normál JMS-ben egyszerűen meghatározunk egy "üzenetaláírás" tulajdonságot, és hozzáadjuk az üzenethez. A Kafka azonban nem ad nekünk mechanizmust a metaadatok átadására, csak egy kulcsot és egy értéket.

Mivel az érték egy banki átutalásos rakomány, amelynek integritását meg akarjuk őrizni, nincs más dolgunk, mint meghatározni a kulcsban használandó adatstruktúrát. Feltételezve, hogy a particionáláshoz fiókazonosítóra van szükségünk, mivel a fiókhoz kapcsolódó összes üzenetet sorrendben kell feldolgozni, a következő JSON-struktúrát fogjuk kitalálni:

{
  "signature": "541661622185851c248b41bf0cea7ad0",
  "accountId": "10007865234"
}

Mivel az aláírás értéke a hasznos terheléstől függően változik, a particionáló felület alapértelmezett kivonatolási stratégiája nem fogja megbízhatóan csoportosítani a kapcsolódó üzeneteket. Ezért meg kell írnunk a saját stratégiánkat, amely elemzi ezt a kulcsot, és felosztja az accountId értéket.

A Kafka ellenőrző összegeket tartalmaz az áruházban lévő üzenetek sérülésének észlelésére, és a biztonsági funkciók teljes készletével rendelkezik. Ennek ellenére néha megjelennek iparág-specifikus követelmények, mint például a fenti.

A felhasználó particionálási stratégiájának biztosítania kell, hogy minden kapcsolódó üzenet ugyanarra a partícióra kerüljön. Bár ez egyszerűnek tűnik, a követelményt bonyolíthatja a kapcsolódó üzenetek sorrendjének fontossága és az, hogy mennyire fix a partíciók száma egy témában.

A partíciók száma egy témában idővel változhat, mivel ezek hozzáadhatók, ha a forgalom meghaladja a kezdeti várakozásokat. Így az üzenetkulcsok társíthatók ahhoz a partícióhoz, amelyre eredetileg küldték őket, ami azt jelenti, hogy egy állapotot meg kell osztani a termelő példányok között.

Egy másik figyelembe veendő tényező az üzenetek egyenletes elosztása a partíciók között. A kulcsok általában nem egyenletesen oszlanak el az üzenetek között, és a hash függvények nem garantálják az üzenetek igazságos elosztását kis kulcskészlet esetén.
Fontos megjegyezni, hogy bárhogyan is választja az üzenetek felosztását, előfordulhat, hogy magát az elválasztót újra fel kell használni.

Vegye figyelembe a különböző földrajzi helyeken található Kafka-klaszterek közötti adatok replikálására vonatkozó követelményt. Erre a célra a Kafka egy MirrorMaker nevű parancssori eszközzel érkezik, amely az egyik fürtből érkező üzenetek olvasására és a másikba való átvitelére szolgál.

A MirrorMakernek meg kell értenie a replikált témakör kulcsait, hogy a fürtök közötti replikáció során fenntartsa az üzenetek közötti relatív sorrendet, mivel előfordulhat, hogy az adott témakör partícióinak száma nem azonos két fürtben.

Az egyéni particionálási stratégiák viszonylag ritkák, mivel az alapértelmezett kivonatolás vagy a körözés a legtöbb esetben jól működik. Ha azonban erős rendelési garanciákra van szüksége, vagy metaadatokat kell kinyernie a hasznos terhekből, akkor érdemes közelebbről megvizsgálnia a particionálást.

A Kafka méretezhetőségi és teljesítménybeli előnyei abból fakadnak, hogy a hagyományos bróker felelősségeinek egy részét az ügyfélre hárítja. Ebben az esetben döntés születik arról, hogy a potenciálisan kapcsolódó üzeneteket több párhuzamosan dolgozó fogyasztó között osztják el.

A JMS-brókereknek is foglalkozniuk kell ilyen követelményekkel. Érdekes módon a JMS üzenetcsoportokon (a ragadós terheléselosztási (SLB) stratégia egy változata) keresztül megvalósított, kapcsolódó üzenetek ugyanannak a fogyasztónak történő elküldésének mechanizmusa megköveteli a feladótól, hogy az üzeneteket kapcsolódóként jelölje meg. A JMS esetében a közvetítő felelős azért, hogy ezt a kapcsolódó üzenetcsoportot a sok közül egy fogyasztónak elküldje, és a csoport tulajdonjogát átruházza, ha a fogyasztó kiesik.

Termelői megállapodások

Üzenetek küldésekor nem csak a particionálást kell figyelembe venni. Nézzük meg a Producer osztály send() metódusait a Java API-ban:

Future < RecordMetadata > send(ProducerRecord < K, V > record);
Future < RecordMetadata > send(ProducerRecord < K, V > record, Callback callback);

Azonnal meg kell jegyezni, hogy mindkét módszer Future-t ad vissza, ami azt jelzi, hogy a küldési művelet nem hajtódik végre azonnal. Az eredmény az, hogy minden aktív partícióhoz egy üzenet (ProducerRecord) kerül a küldési pufferbe, és háttérszálként kerül elküldésre a közvetítőnek a Kafka klienskönyvtárban. Bár ez hihetetlenül felgyorsítja a dolgokat, ez azt jelenti, hogy egy tapasztalatlan alkalmazás üzeneteket veszíthet, ha leállítják a folyamatát.

Mint mindig, most is van mód a küldési művelet megbízhatóbbá tételére a teljesítmény rovására. Ennek a puffernek a mérete 0-ra állítható, és a küldő alkalmazásszál kénytelen lesz megvárni, amíg az üzenet átvitele befejeződik a közvetítő felé, az alábbiak szerint:

RecordMetadata metadata = producer.send(record).get();

További információ az üzenetek olvasásáról

Az üzenetek olvasása további bonyolultságokkal jár, amelyekről találgatni kell. Ellentétben a JMS API-val, amely üzenetfigyelőt tud futtatni válaszként egy üzenetre, a Fogyasztó Kafka csak szavaz. Nézzük meg közelebbről a módszert közvélemény kutatás()erre a célra használják:

ConsumerRecords < K, V > poll(long timeout);

A metódus visszatérési értéke egy több objektumot tartalmazó tárolóstruktúra fogyasztói rekord potenciálisan több partícióból. fogyasztói rekord maga is egy kulcs-érték pár tárolóobjektuma a kapcsolódó metaadatokkal, például a partícióval, amelyből származik.

Ahogy a 2. fejezetben tárgyaltuk, szem előtt kell tartanunk, hogy mi történik az üzenetekkel a sikeres vagy sikertelen feldolgozás után, például ha a kliens nem tudja feldolgozni az üzenetet, vagy ha megszakad. A JMS-ben ezt nyugtázási módban kezelték. A közvetítő vagy törli a sikeresen feldolgozott üzenetet, vagy újra kézbesíti a nyers vagy hamis üzenetet (feltéve, hogy tranzakciókat használtak).
Kafka egészen másképp működik. Az üzenetek a lektorálás után nem törlődnek a közvetítőben, és ami hiba esetén történik, az maga a lektori kód felelőssége.

Mint mondtuk, a fogyasztói csoport az eltoláshoz van társítva a naplóban. Az ehhez az eltoláshoz társított naplópozíció a következő válaszként kiadandó üzenetnek felel meg közvélemény kutatás(). Az olvasás szempontjából meghatározó az az időpont, amikor ez az eltolás nő.

Visszatérve a korábban tárgyalt olvasási modellhez, az üzenetfeldolgozás három szakaszból áll:

  1. Üzenet lekérése elolvasásra.
  2. Az üzenet feldolgozása.
  3. Erősítse meg az üzenetet.

A Kafka fogyasztó konfigurációs opcióval rendelkezik enable.auto.commit. Ez egy gyakran használt alapértelmezett beállítás, csakúgy, mint az "auto" szót tartalmazó beállításoknál.

A Kafka 0.10 előtt az ezt a lehetőséget használó kliens az utolsó olvasott üzenet eltolását küldte el a következő hívásnál közvélemény kutatás() feldolgozás után. Ez azt jelentette, hogy a már letöltött üzenetek újra feldolgozhatók, ha a kliens már feldolgozta őket, de váratlanul megsemmisült a hívás előtt. közvélemény kutatás(). Mivel a bróker nem tart nyilván arról, hogy egy üzenetet hányszor olvastak el, a következő fogyasztó, aki lekéri az üzenetet, nem fogja tudni, hogy semmi rossz történt. Ez a viselkedés pszeudo-tranzakciós volt. Az eltolást csak akkor hajtották végre, ha az üzenet sikeres volt, de ha az ügyfél megszakította, a közvetítő ugyanazt az üzenetet újra elküldi egy másik ügyfélnek. Ez a viselkedés összhangban volt az üzenet kézbesítési garanciájával"legalább egyszer”.

A Kafka 0.10-ben az ügyfélkódot úgy módosították, hogy a véglegesítést az ügyfélkönyvtár időközönként indítsa el, a konfigurált módon auto.commit.interval.ms. Ez a viselkedés valahol a JMS AUTO_ACKNOWLEDGE és a DUPS_OK_ACKNOWLEDGE mód között van. Az automatikus commit használatakor az üzenetek véglegesíthetők, függetlenül attól, hogy ténylegesen feldolgozták-e őket – ez lassú fogyasztó esetén fordulhat elő. Ha egy fogyasztó megszakítja az üzeneteket, az üzeneteket a következő fogyasztó fogja lekérni, az elkötelezett pozíciótól kezdve, ami elmulasztott üzenetet eredményezhet. Ebben az esetben Kafka nem veszítette el az üzeneteket, csak az olvasási kód nem dolgozta fel azokat.

Ennek a módnak ugyanaz az ígérete, mint a 0.9-es verzióban: az üzenetek feldolgozhatók, de ha ez nem sikerül, előfordulhat, hogy az eltolás nem kerül végrehajtásra, ami a kézbesítés megduplázódását okozhatja. Minél több üzenetet tölt le végrehajtáskor közvélemény kutatás(), annál több ez a probléma.

Amint az „Üzenetek olvasása a várólistából”, oldalszám: 21 rész tárgyalja, az üzenetküldő rendszerben nem létezik egyszeri üzenetkézbesítés, ha figyelembe veszik a hibamódokat.

A Kafkában kétféleképpen lehet végrehajtani (commit) egy eltolást (offset): automatikusan és manuálisan. Mindkét esetben az üzenetek többször is feldolgozhatók, ha az üzenet feldolgozása megtörtént, de a véglegesítés előtt meghiúsult. Azt is választhatja, hogy egyáltalán nem dolgozza fel az üzenetet, ha a véglegesítés a háttérben történt, és a kódja a feldolgozás előtt befejeződött (talán a Kafka 0.9 és korábbi verzióiban).

A paraméter beállításával vezérelheti a kézi eltolási véglegesítési folyamatot a Kafka fogyasztói API-ban enable.auto.commit hamis, és kifejezetten meghívja a következő módszerek egyikét:

void commitSync();
void commitAsync();

Ha a "legalább egyszer" üzenetet szeretné feldolgozni, akkor az eltolást kézzel kell végrehajtania a következővel commitSync()az üzenetek feldolgozása után azonnal végrehajtva ezt a parancsot.

Ezek a módszerek nem teszik lehetővé az üzenetek feldolgozás előtti nyugtázását, de semmit sem tesznek annak érdekében, hogy kiküszöböljék a lehetséges feldolgozási késéseket, miközben tranzakciósnak látszanak. Kafkában nincsenek tranzakciók. Az ügyfélnek nincs lehetősége a következőkre:

  • Hamisított üzenet automatikus visszaállítása. A fogyasztóknak maguknak kell kezelniük a problémás rakományokból és háttérkimaradásokból adódó kivételeket, mivel nem számíthatnak a közvetítőre az üzenetek újrakézbesítésében.
  • Üzeneteket küldhet több témához egyetlen atomműveletben. Amint azt hamarosan látni fogjuk, a különböző témák és partíciók vezérlése a Kafka-fürt különböző gépein található, amelyek elküldéskor nem koordinálják a tranzakciókat. A cikk írásakor történt némi munka annak érdekében, hogy ez a KIP-98 segítségével lehetővé váljon.
  • Társítsa egy üzenet elolvasását egy témában egy másik üzenet másik témába való elküldésével. A Kafka architektúrája ismét sok független géptől függ, amelyek egy buszként futnak, és ezt nem próbálják elrejteni. Például nincsenek olyan API-összetevők, amelyek lehetővé tennék az összekapcsolást fogyasztó и termelő tranzakcióban. A JMS-ben ezt az objektum biztosítja Ülésamelyekből létrejönnek MessageProducers и MessageConsumers.

Ha nem hagyatkozhatunk a tranzakciókra, hogyan biztosíthatunk szemantikát, amely közelebb áll a hagyományos üzenetküldő rendszerek által biztosítotthoz?

Ha fennáll annak a lehetősége, hogy a fogyasztó ellentételezése megnőhet az üzenet feldolgozása előtt, például fogyasztói összeomlás során, akkor a fogyasztó nem tudhatja, hogy a fogyasztói csoportja nem fogadta-e el az üzenetet, amikor partíciót rendeltek hozzá. Tehát az egyik stratégia az eltolás visszatekerése az előző pozícióba. A Kafka fogyasztói API a következő módszereket kínálja ehhez:

void seek(TopicPartition partition, long offset);
void seekToBeginning(Collection < TopicPartition > partitions);

módszer keres() módszerrel használható
offsetsForTimes(Térkép időbélyegek kereséshez) visszatekerni egy állapotba a múlt egy bizonyos pontján.

Ennek a megközelítésnek a használata implicit módon azt jelenti, hogy nagyon valószínű, hogy egyes korábban feldolgozott üzeneteket újra elolvasnak és feldolgoznak. Ennek elkerülésére használhatjuk a 4. fejezetben leírt idempotens olvasást a korábban megtekintett üzenetek nyomon követésére és az ismétlődések kiküszöbölésére.

Alternatív megoldásként a fogyasztói kód egyszerű maradhat, mindaddig, amíg az üzenet elvesztése vagy többszörözése elfogadható. Ha figyelembe vesszük a Kafkát általánosan használt használati eseteket, például a naplóesemények, metrikák, kattintáskövetés stb. kezelését, megértjük, hogy az egyes üzenetek elvesztése valószínűleg nem lesz jelentős hatással a környező alkalmazásokra. Ilyen esetekben az alapértelmezett értékek teljesen elfogadhatók. Másrészt, ha az alkalmazásnak kifizetést kell küldenie, gondosan kell kezelnie minden egyes üzenetet. Minden a kontextuson múlik.

Személyes megfigyelések azt mutatják, hogy az üzenetek intenzitásának növekedésével az egyes üzenetek értéke csökken. A nagyméretű üzenetek általában értékesek, ha összesített formában nézik őket.

Magas rendelkezésre állás

A Kafka megközelítése a magas rendelkezésre álláshoz nagyon eltér az ActiveMQ megközelítésétől. A Kafka kibővíthető fürtök köré készült, ahol az összes közvetítőpéldány egyszerre fogadja és terjeszti az üzeneteket.

A Kafka-fürt több közvetítőpéldányból áll, amelyek különböző szervereken futnak. A Kafkát úgy tervezték, hogy közönséges önálló hardveren fusson, ahol minden csomópont saját dedikált tárolóval rendelkezik. A hálózathoz csatolt tároló (SAN) használata nem javasolt, mert több számítási csomópont is versenyezhet az időért.Ыe tárolási intervallumokat és konfliktusokat hoz létre.

Kafka az mindig bekapcsolva rendszer. Sok nagy Kafka-felhasználó soha nem állítja le a fürtöket, és a szoftver mindig szekvenciális újraindítással frissül. Ezt úgy érik el, hogy garantálják a kompatibilitást az előző verzióval az üzenetek és a brókerek közötti interakciók számára.

Szerverfürthöz csatlakozó brókerek Állatgondozó, amely konfigurációs adatnyilvántartásként működik, és az egyes brókerek szerepeinek összehangolására szolgál. A ZooKeeper maga egy elosztott rendszer, amely magas rendelkezésre állást biztosít az információk replikációja révén határozatképesség.

Alapesetben egy témakör jön létre egy Kafka-fürtben a következő tulajdonságokkal:

  • A partíciók száma. Amint azt korábban tárgyaltuk, az itt használt pontos érték a párhuzamos olvasás kívánt szintjétől függ.
  • A replikációs tényező (tényező) határozza meg, hogy a fürtben hány közvetítőpéldány tartalmazzon naplókat ehhez a partícióhoz.

A ZooKeepers segítségével a koordinációra Kafka megpróbálja igazságosan elosztani az új partíciókat a fürt brókerei között. Ezt egyetlen példány végzi, amely vezérlőként működik.

Futásidőben minden témapartícióhoz Vezérlő szerepeket oszt ki egy brókernek vezető (vezető, mester, előadó) ill követői (követők, rabszolgák, beosztottak). A bróker, aki ennek a partíciónak a vezetője, felelős azért, hogy a gyártók által neki küldött összes üzenetet megkapja, és az üzeneteket eljussa a fogyasztókhoz. Amikor üzeneteket küldenek egy témapartícióra, azokat a rendszer az adott partíció követőjeként működő összes közvetítő csomópontra replikálja. Minden egyes partíció naplóit tartalmazó csomópont meghívásra kerül másolat. A bróker egyes partíciók vezetőjeként, mások számára pedig követőként működhet.

Meghívásra kerül egy követő, amely a vezető összes üzenetét tartalmazza szinkronizált replika (szinkronizált állapotban lévő replika, szinkronizált replika). Ha egy partíció vezetőjeként tevékenykedő bróker leáll, bármely naprakész vagy az adott partícióhoz szinkronizált bróker átveheti a vezető szerepet. Ez egy hihetetlenül fenntartható design.

A gyártó konfigurációjának része a paraméter kakasok, amely meghatározza, hogy hány replikának kell nyugtáznia (nyugtáznia) egy üzenet fogadását, mielőtt az alkalmazásszál folytatja a küldést: 0, 1 vagy minden. Ha be van állítva minden, majd üzenet érkezésekor a vezető visszaigazolást küld a producernek, amint megkapja a rekord megerősítését (nyugtázását) a témabeállítás által meghatározott több jelzéstől (beleértve önmagát is). min.insync.replicas (alapértelmezett 1). Ha az üzenet nem replikálható sikeresen, akkor a gyártó alkalmazási kivételt dob ​​(NotEnoughReplicas vagy NotEnoughReplicasAfterAppend).

Egy tipikus konfiguráció létrehoz egy témát 3-as replikációs tényezővel (1 vezető, 2 követő partíciónként) és a paramétert min.insync.replicas Ebben az esetben a fürt lehetővé teszi, hogy a témapartíciót kezelő egyik közvetítő leálljon az ügyfélalkalmazások befolyásolása nélkül.

Ezzel visszatérünk a teljesítmény és a megbízhatóság már megszokott kompromisszumához. A replikáció a követőktől érkező megerősítésekre (visszaigazolásokra) vonatkozó további várakozási idő rovására történik. Bár, mivel párhuzamosan fut, a replikáció legalább három csomópontra ugyanolyan teljesítményű, mint kettő (figyelmen kívül hagyva a hálózati sávszélesség-használat növekedését).

Ennek a replikációs sémának a használatával Kafka ügyesen elkerüli, hogy minden üzenetet fizikailag lemezre írjon a művelettel szinkronizál(). A gyártó által küldött minden üzenet a partíciónaplóba kerül, de a 2. fejezetben leírtak szerint a fájlba írás kezdetben az operációs rendszer pufferében történik. Ha ezt az üzenetet egy másik Kafka-példányra replikálják, és a memóriájában van, a vezető elvesztése nem jelenti azt, hogy maga az üzenet is elveszett – átveheti egy szinkronizált replikával.
A művelet végrehajtásának megtagadása szinkronizál() azt jelenti, hogy Kafka olyan gyorsan tud üzeneteket fogadni, mint amennyire képes a memóriába írni. Ezzel szemben minél tovább kerülheti a memória lemezre öblítését, annál jobb. Emiatt nem ritka, hogy a Kafka brókerek 64 GB vagy több memóriát kapnak. Ez a memóriahasználat azt jelenti, hogy egyetlen Kafka-példány könnyen több ezerszer gyorsabban fut, mint egy hagyományos üzenetközvetítő.

A Kafka a művelet alkalmazására is beállítható szinkronizál() üzenetcsomagokhoz. Mivel a Kafkában minden csomag-orientált, valójában nagyon jól működik számos felhasználási esetben, és hasznos eszköz a nagyon erős garanciákat igénylő felhasználók számára. A Kafka tiszta teljesítményének nagy része azokból az üzenetekből származik, amelyeket csomagként küldenek a brókernek, és ezeket az üzeneteket a bróker szekvenciális blokkokban olvassa be. nulla másolat műveletek (olyan műveletek, amelyek során az adatok egyik memóriaterületről a másikra történő másolása nem történik meg). Ez utóbbi nagy teljesítmény- és erőforrás-növekedést jelent, és csak a partíciós sémát meghatározó mögöttes naplóadat-struktúra használatával lehetséges.

Sokkal jobb teljesítmény érhető el egy Kafka-fürtben, mint egyetlen Kafka-brókerrel, mivel a témapartíciók sok különálló gépen skálázhatók.

Eredményei

Ebben a fejezetben megvizsgáltuk, hogy a Kafka architektúra hogyan képzeli újra az ügyfelek és a közvetítők közötti kapcsolatot, hogy egy hihetetlenül robusztus üzenetküldési folyamatot biztosítson, amelynek átviteli sebessége többszöröse a hagyományos üzenetközvetítőkének. Megbeszéltük, hogy milyen funkciókat használ ennek eléréséhez, és röviden áttekintettük az ezt a funkciót biztosító alkalmazások architektúráját. A következő fejezetben áttekintjük azokat a gyakori problémákat, amelyeket az üzenetküldő alapú alkalmazásoknak meg kell oldaniuk, és megvitatjuk a kezelési stratégiákat. A fejezetet azzal zárjuk, hogy felvázoljuk, hogyan beszéljünk az üzenetkezelési technológiákról általánosságban, hogy felmérhesse, mennyire alkalmasak az Ön használati eseteire.

Előző lefordított rész: Az üzenetközvetítők megértése. Az üzenetküldés mechanikájának elsajátítása az ActiveMQ-val és a Kafkával. 1. fejezet

A fordítás elkészült: tele.gg/middle_java

Folytatás ...

A felmérésben csak regisztrált felhasználók vehetnek részt. Bejelentkezés, kérem.

Használják a Kafkát a szervezetében?

  • Igen

  • Nincs

  • Korábban használt, most nem

  • Tervezzük használni

38 felhasználó szavazott. 8 felhasználó tartózkodott.

Forrás: will.com

Hozzászólás