paint-brush
Spolehlivé zasílání zpráv v distribuovaných systémechpodle@fairday
37,190 čtení
37,190 čtení

Spolehlivé zasílání zpráv v distribuovaných systémech

podle Aleksei8m2024/03/18
Read on Terminal Reader
Read this story w/o Javascript

Příliš dlouho; Číst

Vybudování spolehlivého, vysoce dostupného a škálovatelného distribuovaného systému vyžaduje dodržování specifických technik, principů a vzorů.
featured image - Spolehlivé zasílání zpráv v distribuovaných systémech
Aleksei HackerNoon profile picture

Problém s duálním zápisem

Vybudování spolehlivého, vysoce dostupného a škálovatelného distribuovaného systému vyžaduje dodržování specifických technik, principů a vzorů. Návrh takových systémů zahrnuje řešení nesčetných výzev. Mezi nejrozšířenější a základní problémy patří problém duálního zápisu .


„Problém duálního zápisu“ je problém, který se objevuje v distribuovaných systémech, zejména při práci s více zdroji dat nebo databázemi, které je třeba udržovat v synchronizaci. Odkazuje na obtížnost zajištění toho, aby byly změny dat konzistentně zapisovány do různých datových úložišť, jako jsou databáze nebo mezipaměti, aniž by docházelo k problémům, jako jsou nekonzistence dat, konflikty nebo úzká místa výkonu.


Architektura mikroslužeb a databáze vzorů na službu vám přináší mnoho výhod, jako je nezávislé nasazení a škálování, izolovaná selhání a potenciální zvýšení rychlosti vývoje. Operace však vyžadují změny mezi více mikroslužbami, což vás nutí přemýšlet o spolehlivém řešení tohoto problému.

Téměř reálný příklad

Podívejme se na scénář, ve kterém naše doména zahrnuje přijímání žádostí o úvěr, jejich posuzování a následné zasílání upozornění na upozornění zákazníkům.


V duchu principu jediné odpovědnosti, Conwayova zákona a přístupu k návrhu řízenému doménou, byla po několika relacích událostmi bouře celá doména rozdělena na tři subdomény s definovanými ohraničenými kontexty s jasnými hranicemi, doménovými modely a všudypřítomným jazykem.


První má za úkol onboarding a sestavování nových žádostí o půjčku. Druhý systém tyto žádosti vyhodnocuje a rozhoduje na základě poskytnutých dat. Tento proces hodnocení, včetně kontroly KYC/KYB, ochrany proti podvodům a úvěrového rizika, může být časově náročný a vyžaduje schopnost zpracovávat tisíce aplikací současně. V důsledku toho byla tato funkce delegována na vyhrazenou mikroslužbu s vlastní databází, která umožňuje nezávislé škálování.

Kromě toho jsou tyto subsystémy spravovány dvěma různými týmy, z nichž každý má své vlastní cykly vydávání, smlouvy o úrovni služeb (SLA) a požadavky na škálovatelnost.


A konečně , specializovaná oznamovací služba je k dispozici pro zasílání upozornění zákazníkům.



Zde je podrobný popis primárního případu použití systému:

  1. Zákazník podá žádost o úvěr.
  2. Služba žádostí o půjčku zaznamená novou žádost se stavem „Nevyřízeno“ a zahájí proces posouzení předáním žádosti hodnotící službě.
  3. Assessment Service příchozí žádost o úvěr vyhodnotí a následně o svém rozhodnutí informuje Službu žádosti o úvěr.
  4. Po obdržení rozhodnutí služba žádosti o půjčku odpovídajícím způsobem aktualizuje stav žádosti o půjčku a spustí službu oznámení, aby informovala zákazníka o výsledku.
  5. Služba Upozornění zpracuje tento požadavek a odešle zákazníkovi upozornění prostřednictvím e-mailu, SMS nebo jiným preferovaným způsobem komunikace podle nastavení zákazníka.


Je to na první pohled docela jednoduchý a primitivní systém, ale pojďme se vrhnout na to, jak služba Žádost o půjčku zpracovává příkaz k podání žádosti o půjčku.


Můžeme zvážit dva přístupy k interakcím služeb:

  1. First-Local-Commit-Then-Publish: V tomto přístupu služba aktualizuje svou místní databázi (potvrdí) a poté publikuje událost nebo zprávu dalším službám.

  2. First-Publish-Then-Local-Commit: Tato metoda naopak zahrnuje publikování události nebo zprávy před potvrzením změn do místní databáze.


Obě metody mají své nevýhody a jsou pouze částečně bezpečné pro komunikaci v distribuovaných systémech.


Toto je sekvenční diagram aplikace prvního přístupu.


Nejprve-místně-zavázat-pak-publikovat


V tomto scénáři služba Loan Application Service využívá přístup First-Local-Commit-Then-Publish , kdy nejprve potvrdí transakci a poté se pokusí odeslat upozornění do jiného systému. Tento proces však může selhat například v případě, že se vyskytnou problémy se sítí, služba Assessment Service je nedostupná nebo služba Loan Application Service narazí na chybu Nedostatek paměti (OOM) a dojde k jejímu zhroucení. V takových případech by se zpráva ztratila a posouzení by zůstalo bez upozornění na novou žádost o úvěr, pokud nebudou zavedena další opatření.


A ten druhý.

Nejprve-publikujte-potom-místně-závazek
Ve scénáři First-Publish-Then-Local-Commit čelí služba žádosti o úvěr významnějším rizikům. Může informovat Assessment Service o nové aplikaci, ale nebude možné tuto aktualizaci lokálně uložit kvůli problémům, jako jsou problémy s databází, chyby paměti nebo chyby v kódu. Tento přístup může vést k významným nekonzistentnostem v datech, což může způsobit vážné problémy v závislosti na tom, jak služba Loan Review Service nakládá s příchozími aplikacemi.


Proto musíme najít řešení, které nabízí robustní mechanismus pro zveřejňování událostí externím spotřebitelům. Než se však ponoříme do potenciálních řešení, měli bychom si nejprve ujasnit typy záruk doručování zpráv, kterých lze v distribuovaných systémech dosáhnout.

Garance doručení zpráv

Můžeme dosáhnout čtyř typů záruk.

  1. Žádné záruky
    Neexistuje žádná záruka, že zpráva bude doručena na místo určení. Přesně o tom je přístup First-Local-Commit-Then-Publish . Spotřebitelé mohou přijímat zprávy jednou, vícekrát nebo vůbec nikdy.

  2. Maximálně jednou doručení
    Maximálně jednou doručení znamená, že zpráva bude doručena na místo určení nejvýše 1krát. Přístup First-Local-Commit-Then-Publish lze implementovat tímto způsobem také s politikou opakování pokusů s hodnotou jedna.

  3. Alespoň jednou delivery\Consumers přijme a zpracuje každou zprávu, ale stejnou zprávu může obdržet více než jednou.

  4. Přesně jednou doručení\Přesně jednou doručení znamená, že spotřebitel obdrží zprávu efektivně jednou.
    Technicky je možné dosáhnout s Kafka transakcemi a konkrétní idempotentní implementací výrobce a spotřebitele.


Ve většině případů záruky doručení „alespoň jednou“ řeší mnoho problémů tím, že zajišťují, aby zprávy byly doručeny alespoň jednou, ale spotřebitelé musí být idempotentní. Avšak vzhledem k nevyhnutelným selháním sítě musí být veškerá spotřebitelská logika idempotentní, aby se zabránilo zpracování duplicitních zpráv, bez ohledu na záruky výrobce. Tento požadavek tedy není ani tak nevýhodou, jako spíše odráží realitu.

Řešení

Existuje spousta řešení tohoto problému, která mají své výhody i nevýhody.

Dvoufázové potvrzení

Podle Wikipedie je Two-Phase Commit (2PC) distribuovaný transakční protokol používaný v informatice a systémech správy databází k zajištění konzistence a spolehlivosti distribuovaných transakcí. Je navržen pro situace, kdy se na jedné transakci musí podílet více zdrojů (např. databáze), a zajišťuje, že buď všechny transakci potvrdí, nebo ji všechny přeruší, čímž je zachována konzistence dat. Zní to přesně tak, jak potřebujeme, ale dvoufázový závazek má několik nevýhod:

  • Pokud jeden zúčastněný zdroj přestane reagovat nebo dojde k selhání, může být celý proces zablokován, dokud nebude problém vyřešen. To může vést k potenciálním problémům s výkonem a dostupností.
  • Two-Phase Commit neposkytuje vestavěné mechanismy odolnosti proti chybám. Při řešení poruch se spoléhá na externí mechanismy nebo ruční zásah.
  • Ne všechny moderní databáze podporují dvoufázový závazek.

Sdílená databáze

Nejviditelnějším řešením pro architekturu mikroslužeb je použít vzor (nebo dokonce někdy anti-vzor) – sdílenou databázi. Tento přístup je velmi intuitivní, pokud potřebujete transakční konzistenci napříč více tabulkami v různých databázích, stačí pro tyto mikroslužby použít jednu sdílenou databázi.


Mezi nevýhody tohoto přístupu patří zavedení jediného bodu selhání, omezení nezávislého škálování databáze a omezení možnosti používat různá databázová řešení, která jsou nejvhodnější pro konkrétní požadavky a případy použití. Pro podporu takovéto formy distribuované transakce by navíc byly nutné úpravy kódových bází mikroslužeb.

Transakční pošta k odeslání

Transakční pošta k odeslání “ je návrhový vzor používaný v distribuovaných systémech k zajištění spolehlivého šíření zpráv, a to i v případě nespolehlivých systémů zasílání zpráv. Zahrnuje ukládání událostí do určené tabulky 'OutboxEvents' v rámci stejné transakce jako samotná operace. Tento přístup je v souladu s vlastnostmi ACID relačních databází. Naproti tomu mnoho databází No-SQL plně nepodporuje vlastnosti ACID a místo toho volí principy teorému CAP a filozofie BASE, které upřednostňují dostupnost a případnou konzistenci před přísnou konzistencí.


Transakční outbox poskytuje alespoň jednou záruku a může být implementován několika přístupy:

  1. Odpad transakčního protokolu

  2. Vydavatel ankety


Přístup tailing protokolu transakcí zahrnuje použití řešení specifických pro databázi, jako je CDC (Change Data Capture). Hlavní nevýhody tohoto přístupu jsou:

  • Databázová specifická řešení

  • Zvýšená latence kvůli specifikům implementací CDC


Další metodou je aplikace Polling Publisher , která usnadňuje stahování pošty k odeslání dotazováním tabulky pošty k odeslání. Primární nevýhodou tohoto přístupu je potenciál pro zvýšené zatížení databáze, což může vést k vyšším nákladům. Kromě toho ne všechny databáze No-SQL podporují efektivní dotazování na konkrétní segmenty dokumentů. Extrahování celých dokumentů může proto vést ke snížení výkonu.


Zde je malý sekvenční diagram vysvětlující, jak to funguje.


Poslouchejte sami sebe

Primární problém se vzorem Transakční Outbox spočívá v jeho závislosti na vlastnostech databáze ACID. V typických databázích OLTP to může být jednoduché, ale ve sféře NoSQL představuje problémy. K vyřešení tohoto problému je potenciálním řešením využít protokol připojení (například Kafka) hned od zahájení zpracování požadavku.


Místo přímého zpracování příkazu „odeslání žádosti o půjčku“ jej ihned odešleme do interního Kafkova tématu a následně klientovi vrátíme „přijatý“ výsledek. Protože je však vysoce pravděpodobné, že příkaz ještě musí být zpracován, nemůžeme zákazníka okamžitě informovat o výsledku. Ke správě této případné konzistence můžeme použít techniky, jako je dlouhé dotazování, dotazování iniciované klientem, optimistické aktualizace uživatelského rozhraní nebo používání WebSockets nebo Server-Sent Events pro oznámení. Toto je však zcela odlišné téma, takže se vraťme k původnímu tématu.


Poslali jsme zprávu na interní kafkovské téma. Služba Loan Application Service poté spotřebuje tuto zprávu – stejný příkaz, který obdržela od klienta – a zahájí zpracování. Za prvé, provádí nějakou obchodní logiku; teprve poté, co je tato logika úspěšně provedena a výsledky jsou trvalé, publikuje nové zprávy na veřejné téma Kafka.


Pojďme se podívat na trochu pseudokódu.


 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(); }


Co když selže zpracování obchodní logiky? Žádný strach, protože offset ještě nebyl potvrzen, zpráva bude zopakována.


Co když odesílání nových událostí Kafkovi selže? Žádný strach, protože obchodní logika je idempotentní, nevytvoří duplicitní žádost o úvěr. Místo toho se pokusí znovu odeslat zprávy na veřejné téma Kafka.


Co když jsou zprávy odeslány Kafkovi, ale kompenzace selže? Žádný strach, protože obchodní logika je idempotentní, nevytvoří duplicitní žádost o úvěr. Místo toho bude znovu posílat zprávy na veřejné téma Kafka a doufat, že offsetový commit tentokrát uspěje.


Mezi hlavní nevýhody tohoto přístupu patří přidaná složitost spojená s novým stylem programování, případná konzistence (protože klient nebude okamžitě znát výsledek) a požadavek, aby byla veškerá obchodní logika idempotentní.

Zdroj událostí

Co je to sourcing událostí a jak by se dal použít zde? Event sourcing je softwarový architektonický vzor používaný k modelování stavu systému zachycováním všech změn v jeho datech jako série neměnných událostí. Tyto události představují fakta nebo stavové přechody a slouží jako jediný zdroj pravdy pro aktuální stav systému. Technicky tedy implementací systému sourcingu událostí již máme všechny události v EventStore a tento EventStore mohou spotřebitelé používat jako jediný zdroj pravdy o tom, co se stalo. Není potřeba specifické databázové řešení pro sledování všech změn nebo obav z objednávání, jediným problémem je sezení na straně čtení, protože k získání aktuálního stavu entity je nutné přehrát všechny události.

Závěr

V tomto článku jsme zhodnotili několik přístupů k budování spolehlivého zasílání zpráv v distribuovaných systémech. Existuje několik doporučení, která bychom mohli zvážit při budování systémů s těmito vlastnostmi

  1. Vždy vyvíjejte idempotentní spotřebitele, protože selhání sítě je nevyhnutelné.
  2. Pečlivě používejte First-Local-Commit-Then-Publish s jasným pochopením požadavků na záruku.
  3. Nikdy nepoužívejte přístup First-Publish-Then-Local-Commit, protože to může vést k vážné nekonzistenci dat ve vašem systému.
  4. Pokud se rozhodnutí o výběru stávající databáze velmi pravděpodobně může změnit nebo technická strategie znamená vybrat nejlepší řešení úložiště pro daný problém – nevytvářejte sdílené knihovny navázáním na databázová řešení, jako je CDC .
  5. Použijte přístup Transakční Outbox jako standardní řešení pro dosažení alespoň jednou záruk.
  6. Zvažte použití přístupu Poslouchejte sami sebe, když se využívají databáze bez SQL.


Příště se podíváme na praktičtější příklad implementace Transakční pošty k odeslání. Vidět

Vy!