Megbízható, magas rendelkezésre állású, méretezhető elosztott rendszer felépítéséhez meghatározott technikák, elvek és minták betartása szükséges. Az ilyen rendszerek tervezése számtalan kihívással jár. A legelterjedtebb és alapvető problémák közé tartozik a kettős írási probléma .
A „kettős írási probléma” egy olyan kihívás, amely elosztott rendszerekben merül fel, főleg akkor, ha több adatforrással vagy adatbázissal foglalkoznak, amelyeket szinkronban kell tartani. Arra utal, hogy nehéz biztosítani, hogy az adatok változásait következetesen beírják a különböző adattárakba, például adatbázisokba vagy gyorsítótárakba, anélkül, hogy olyan problémákat okoznának, mint az adatok következetlenségei, ütközései vagy teljesítménybeli szűk keresztmetszetek.
A mikroszolgáltatások architektúrája és szolgáltatásonkénti mintaadatbázisa számos előnnyel jár, mint például a független üzembe helyezés és méretezés, az elszigetelt hibák és a fejlesztési sebesség potenciális fellendítése. A műveletek azonban változtatásokat igényelnek több mikroszolgáltatás között, ami arra kényszeríti Önt, hogy gondolkodjon egy megbízható megoldáson a probléma kezelésére.
Tekintsünk egy olyan forgatókönyvet, amelyben a tartományunk magában foglalja a hitelkérelmek elfogadását, elbírálását, majd értesítő riasztások küldését az ügyfeleknek.
Az egységes felelősség elvének, a Conway-törvénynek és a tartományvezérelt tervezési megközelítésnek a szellemében több esemény-viharos munkamenet után az egész tartományt három altartományra osztották, meghatározott korlátos kontextusokkal, egyértelmű határokkal, tartománymodellekkel és mindenütt jelenlévő nyelvvel.
Az első feladata az új hitelkérelmek beépítése és összeállítása. A második rendszer ezeket a pályázatokat értékeli, és a megadott adatok alapján dönt. Ez az értékelési folyamat, beleértve a KYC/KYB-t, a csalás elleni és a hitelkockázati ellenőrzéseket is, időigényes lehet, ezért több ezer kérelem egyidejű kezelésének képességére van szükség. Következésképpen ezt a funkciót egy saját adatbázissal rendelkező, dedikált mikroszolgáltatásra delegálták, amely lehetővé teszi a független méretezést.
Ezen túlmenően ezeket az alrendszereket két különböző csapat kezeli, amelyek mindegyike saját kiadási ciklussal, szolgáltatásszint-szerződéssel (SLA) és méretezhetőségi követelményekkel rendelkezik.
Végül egy speciális értesítési szolgáltatás is működik, amely riasztásokat küld az ügyfeleknek.
Itt található a rendszer elsődleges használati esetének finomított leírása:
Első pillantásra meglehetősen egyszerű és primitív rendszer, de nézzük meg, hogyan dolgozza fel a Hiteligénylés szolgáltatás a hiteligénylés benyújtása parancsot.
A szolgáltatási interakciók két megközelítését tekinthetjük:
Első-helyi véglegesítés, majd közzététel: Ebben a megközelítésben a szolgáltatás frissíti a helyi adatbázisát (commit), majd eseményt vagy üzenetet tesz közzé más szolgáltatások számára.
Első közzététel, majd helyi véglegesítés: Ez a módszer egy esemény vagy üzenet közzétételét foglalja magában, mielőtt a változtatásokat véglegesítené a helyi adatbázisban.
Mindkét módszernek megvannak a maga hátrányai, és csak részben hibabiztosak az elosztott rendszerekben történő kommunikációhoz.
Ez az első megközelítés alkalmazásának szekvenciadiagramja.
Ebben a forgatókönyvben a kölcsönigénylési szolgáltatás a First-Local-Commit-Then-Publish megközelítést alkalmazza, ahol először végrehajt egy tranzakciót, majd megpróbál értesítést küldeni egy másik rendszernek. Ez a folyamat azonban meghibásodhat, ha például hálózati problémák vannak, az Értékelési szolgáltatás nem érhető el, vagy a Hiteligénylési szolgáltatás Memória megtelt (OOM) hibát észlel, és összeomlik. Ilyen esetekben az üzenet elveszne, és az Értékelés az új hiteligénylés értesítése nélkül maradna, hacsak nem hajtanak végre további intézkedéseket.
És a második.
Az első közzététel, majd a helyi kötelezettségvállalás forgatókönyve esetén a kölcsönigénylési szolgáltatás jelentősebb kockázatokkal néz szembe. Előfordulhat, hogy értesíti az Értékelési szolgáltatást egy új alkalmazásról, de nem tudja helyileg menteni a frissítést olyan problémák miatt, mint például adatbázis-problémák, memóriahibák vagy kódhibák. Ez a megközelítés jelentős inkonzisztenciákhoz vezethet az adatokban, ami komoly problémákat okozhat attól függően, hogy a Hitelbírálati szolgáltatás hogyan kezeli a beérkező kérelmeket.
Ezért olyan megoldást kell azonosítanunk, amely robusztus mechanizmust kínál az események külső fogyasztók számára történő közzétételére. Mielőtt azonban belemerülnénk a lehetséges megoldásokba, először tisztázni kell az elosztott rendszerekben elérhető üzenetküldési garanciák típusait.
Négyféle garanciát tudunk elérni.
Nincs garancia
Nincs garancia arra, hogy az üzenetet a célállomásra kézbesítik. A First-Local-Commit-Then-Publish megközelítés pontosan erről szól. A fogyasztók egyszer, többször vagy egyáltalán nem kaphatnak üzeneteket.
Legfeljebb egyszeri szállítás
Legfeljebb egyszeri kézbesítés azt jelenti, hogy az üzenetet legfeljebb 1 alkalommal kézbesítik a célállomásra. A First-Local-Commit-Then-Publish megközelítés ilyen módon is megvalósítható az egyes értékű próbálkozások újrapróbálkozási politikájával.
Legalább egyszer kézbesítés\A fogyasztók minden üzenetet megkapnak és feldolgoznak, de előfordulhat, hogy ugyanazt az üzenetet többször is megkapják.
Pontosan egyszeri kézbesítés\Pontosan egyszeri kézbesítés azt jelenti, hogy a fogyasztó ténylegesen egyszer kapja meg az üzenetet.
Technikailag a Kafka-tranzakciókkal és a termelő és fogyasztó konkrét idempotens megvalósításával lehet elérni.
A legtöbb esetben a „legalább egyszeri” kézbesítési garanciák számos problémát megoldanak azáltal, hogy az üzeneteket legalább egyszer kézbesítik, de a fogyasztóknak tehetetlennek kell lenniük. Az elkerülhetetlen hálózati hibák miatt azonban minden fogyasztói logikának idempotensnek kell lennie, hogy elkerülje a duplikált üzenetek feldolgozását, függetlenül a gyártó garanciáitól. Ezért ez a követelmény nem annyira hátrány, mint inkább a valóságot tükrözi.
Számos megoldás létezik erre a problémára, amelyeknek megvannak a maga előnyei és hátrányai.
A Wikipédia szerint a Two-Phase Commit (2PC) egy elosztott tranzakciós protokoll, amelyet a számítástechnikában és az adatbázis-kezelő rendszerekben használnak az elosztott tranzakciók konzisztenciájának és megbízhatóságának biztosítására. Olyan helyzetekre tervezték, amikor több erőforrásnak (pl. adatbázisnak) kell részt vennie egyetlen tranzakcióban, és biztosítja, hogy vagy mindegyik végrehajtja a tranzakciót, vagy mindegyik megszakítja azt, ezáltal megőrzi az adatok konzisztenciáját. Pontosan úgy hangzik, amire szükségünk van, de a kétfázisú véglegesítésnek számos hátránya van:
A mikroszolgáltatási architektúra legszembetűnőbb megoldása egy minta (vagy akár néha anti-minta) alkalmazása – egy megosztott adatbázis. Ez a megközelítés nagyon intuitív, ha tranzakciós konzisztenciára van szüksége a különböző adatbázisok több táblájában, csak egyetlen megosztott adatbázist használjon ezekhez a mikroszolgáltatásokhoz.
Ennek a megközelítésnek a hátrányai közé tartozik az egyetlen hibapont bevezetése, a független adatbázis-méretezés gátlása, valamint a speciális követelményeknek és használati eseteknek leginkább megfelelő különböző adatbázis-megoldások használatának korlátozása. Ezenkívül a mikroszolgáltatási kódbázisok módosítására lenne szükség az elosztott tranzakciók ilyen formájának támogatásához.
A " tranzakciós kimenő postafiók " egy olyan tervezési minta, amelyet elosztott rendszerekben használnak, hogy megbízható üzenettovábbítást biztosítsanak még megbízhatatlan üzenetküldő rendszerek esetén is. Ez magában foglalja az események tárolását egy kijelölt „OutboxEvents” táblában, ugyanazon a tranzakción belül, mint maga a művelet. Ez a megközelítés jól illeszkedik a relációs adatbázisok ACID tulajdonságaihoz. Ezzel szemben sok No-SQL adatbázis nem támogatja teljes mértékben az ACID tulajdonságokat, ehelyett a CAP tétel és a BASE filozófia elveit választják, amelyek a rendelkezésre állást és az esetleges konzisztenciát helyezik előtérbe a szigorú konzisztenciával szemben.
A tranzakciós kimenő postafiók legalább egyszeri garanciát nyújt, és többféle megközelítéssel is megvalósítható:
Tranzakciós napló farka
Szavazás kiadója
A tranzakciónapló-farok megközelítés magában foglalja az adatbázis-specifikus megoldások, például a CDC (Change Data Capture) használatát. Ennek a megközelítésnek a fő hátrányai a következők:
Adatbázis-specifikus megoldások
Megnövekedett késleltetés a CDC-megvalósítások sajátosságai miatt
Egy másik módszer a Polling Publisher , amely a kimenő levelek táblázat lekérdezésével megkönnyíti a kimenő levelek kitöltését. Ennek a megközelítésnek az elsődleges hátránya a megnövekedett adatbázis-terhelés lehetősége, ami magasabb költségekhez vezethet. Ezenkívül nem minden No-SQL adatbázis támogatja az adott dokumentumszegmensek hatékony lekérdezését. A teljes dokumentumok kibontása ezért a teljesítmény romlását eredményezheti.
Itt van egy kis sorozatdiagram, amely elmagyarázza, hogyan működik.
A Tranzakciós kimenő minta elsődleges kihívása az adatbázis ACID tulajdonságaitól való függésében rejlik. Ez egyszerű lehet a tipikus OLTP-adatbázisokban, de kihívásokat jelent a NoSQL-területen. Ennek megoldására egy lehetséges megoldás a hozzáfűzési napló (például Kafka) kihasználása közvetlenül a kérésfeldolgozás kezdeményezésétől.
A „hitelkérelem benyújtása” parancs közvetlen feldolgozása helyett azonnal elküldjük egy belső Kafka-témába, majd az „elfogadott” eredményt visszaküldjük az ügyfélnek. Mivel azonban nagy a valószínűsége annak, hogy a parancsot még feldolgozni kell, nem tudjuk azonnal tájékoztatni az ügyfelet az eredményről. Ennek az esetleges konzisztenciának a kezelésére olyan technikákat alkalmazhatunk, mint a hosszú lekérdezés, az ügyfél által kezdeményezett lekérdezés, az optimista felhasználói felület frissítése, vagy a WebSockets vagy a Server-Sent Events használata az értesítésekhez. Ez azonban teljesen külön téma, úgyhogy térjünk vissza a kezdeti témánkhoz.
Az üzenetet egy belső Kafka témában küldtük. A Hiteligénylési szolgáltatás ezután felhasználja ezt az üzenetet – ugyanazt a parancsot, amelyet az ügyféltől kapott – és megkezdi a feldolgozást. Először is végrehajt bizonyos üzleti logikát; csak miután ez a logika sikeresen végrehajtódott és az eredmények megmaradtak, új üzeneteket tesz közzé egy nyilvános Kafka-témában.
Vessünk egy pillantást a pszeudokódra.
public async Task HandleAsync(SubmitLoanApplicationCommand command, ...) { //First, process business logic var loanApplication = await _loanApplicationService.HandleCommandAsync(command, ...); //Then, send new events to public Kafka topic producer.Send(new LoanApplicationSubmittedEvent(loanApplication.Id)); //Then, commit offset consumer.Commit(); }
Mi van, ha az üzleti logika feldolgozása meghiúsul? Aggodalomra semmi ok, mivel az ellentételezés még nem történt meg, az üzenetet újra megpróbáljuk.
Mi van, ha új események küldése Kafkának nem sikerül? Aggodalomra semmi ok, mivel az üzleti logika idempotens, nem hoz létre duplikált hitelkérelmet. Ehelyett megpróbálja újra elküldeni az üzeneteket a nyilvános Kafka-témának.
Mi van akkor, ha üzeneteket küldenek Kafkának, de az offset véglegesítés sikertelen? Nem kell aggódni, mivel az üzleti logika idempotens, nem hoz létre duplikált hitelkérelmet. Ehelyett újra üzeneteket küld a nyilvános Kafka-témának, és reméli, hogy az ellentételezési kötelezettség ezúttal sikeres lesz.
Ennek a megközelítésnek a fő hátrányai közé tartozik az új programozási stílushoz kapcsolódó bonyolultság, az esetleges konzisztencia (mivel az ügyfél nem fogja azonnal tudni az eredményt), valamint az a követelmény, hogy minden üzleti logikának idempotensnek kell lennie.
Mi az az eseménybeszerzés, és hogyan lehetne itt alkalmazni? Az eseményforrás egy szoftver-architektúra minta, amelyet a rendszer állapotának modellezésére használnak úgy, hogy az adatok minden változását megváltoztathatatlan események sorozataként rögzítik. Ezek az események tényeket vagy állapotátmeneteket jelentenek, és az igazság egyetlen forrásaként szolgálnak a rendszer jelenlegi állapotához. Technikailag tehát egy eseményforrás-rendszer bevezetésével már minden esemény megvan az EventStore-ban, és ezt az EventStore-t a fogyasztók egyetlen igazságforrásként használhatják a történtekről. Nincs szükség speciális adatbázis-megoldásra az összes változás vagy a rendelési aggályok nyomon követésére, az egyetlen probléma az olvasási oldalon van, mivel az entitás aktuális állapotának lekéréséhez az összes eseményt újra kell játszani.
Ebben a cikkben több megközelítést is áttekintettünk a megbízható üzenetkezelés elosztott rendszerekben történő létrehozására. Számos javaslatot érdemes figyelembe venni, amikor ilyen jellemzőkkel rendelkező rendszereket építünk
Legközelebb egy gyakorlatiasabb példát tekintünk meg a Tranzakciós kimenő postafiók megvalósítására. Lásd
te!