Způsoby uložení dat v aplikacích založených na mikroslužbách

23. 5. 2019
Doba čtení: 23 minut

Sdílet

Dnes si vysvětlíme, které problémy je nutné řešit, když bude každá mikroslužba obsahovat svoji databázi. Rozdělením původně monolitické databáze totiž ztratíme některé důležité vlastnosti (ACID), které je užitečné nějak nahradit.

Obsah

1. Některé možné návrhy architektury aplikací založených na mikroslužbách

2. Zamyšlení před přechodem na řešení založené na mikroslužbách…

3. Distribuovaná datová úložiště

4. Příklad databáze v monolitické aplikaci s implementací e-shopu

5. Některé užitečné vlastnosti databáze použité v monolitické aplikaci

6. Databáze v aplikaci postavené na mikroslužbách

7. Dvoufázový commit v mikroslužbách?

8. Další možnost: kompenzační transakce

9. Použití nástrojů pro streaming: záznam a přehrávání sekvence událostí

10. Koncepty, na nichž je streaming založen

11. Nejznámější systémy podporující streaming

12. Apache Kafka

13. Nahrávání a přehrávání událostí při použití distribuovaných databází

14. Architektura kappa

15. Příklad architektury kappa

16. Přednosti architektury kappa

17. Odkazy na Internetu

1. Některé možné návrhy architektury aplikací založených na mikroslužbách

V úvodním článku jsme se seznámili s některými základními koncepty, na nichž jsou postaveny mikroslužby. Připomeňme si, že cílem návrhu architektury aplikace založené na mikroslužbách je dosažení co nejlepší izolace jednotlivých mikroslužeb. Mikroslužby by v naprosté většině případů neměly sdílet databáze a komunikovat by spolu měly pouze přes API volané přes síťové protokoly (nikoli přímo, jak je to možné při běhu na jediném počítači a operačním systému; samozřejmě ani není možné pro komunikaci použít sdílenou paměť). Ovšem ještě lepší bývá stav, kdy spolu služby raději vůbec nekomunikují :-), přesněji řečeno když komunikují pouze přes nějakého prostředníka, kterým může být message bus, message broker, systém pro správu událostí atd.

„It must be possible to feed a team that maintains a service with two pizzas.“
Werner Vogels

Řešení postavené na mikroslužbách přináší některé výhody, například možnost používat různé programovací jazyky a knihovny pro jednotlivé mikroslužby (zmenší se tím pravděpodobnost, že celá obrovská aplikace morálně zastará), provádět paralelní vývoj s několika samostatnými týmy s omezením komunikace mezi těmito týmy, nasazovat jednotlivé služby samostatně, zlepšují se možnosti load balancingu, škálovatelnosti atd. A samozřejmě, protože v IT nedostaneme nic zadarmo, přináší mikroslužby pochopitelně i celou řadu problémů. Dnes se budeme zabývat tím vůbec nejpalčivějším problémem – co s daty, resp. přesněji řečeno jak řešit změnu stavu celé aplikace.

Partner seriálu o mikroslužbách

V IGNUM mají rádi technologie a staví na nich vlastní mikroslužby, díky kterým je registrace a správa domén, hostingů a e-mailů pro zákazníky hračka. Vymýšlet jednoduchá řešení pro obsluhu složitých systémů a uvádět je v život je výzva. Společnost IGNUM miluje mikroslužby a je proto hrdým partnerem tohoto seriálu.

2. Zamyšlení před přechodem na řešení založené na mikroslužbách…

„People try to copy Netflix, but they can only copy what they see. They copy the results, not the process“
Adrian Cockcroft, former Netflix Chief Cloud Architect

Před případným přechodem z monolitických aplikací na mikroslužby je v první řadě dobré si uvědomit jednu důležitou věc. Uživatelské služby typu Netflix a LinkedIn skutečně mikroslužby velmi úspěšně používají (pravděpodobně by ani s jinou architekturou nemohly uspět), ale je tomu tak mj. i z toho důvodu, že typ aplikací/služeb, které provozují, má v podstatě dosti jednoduchou strukturu (stavový prostor), kterou lze relativně snadno rozdělit na mikroslužby. Naproti tomu běžné dennodenně provozované podnikové aplikace sice nemusí dodávat videa nebo publikovat tweeety stovkám milionů uživatelů, ale struktura a provázanost dat v nich bývá mnohem větší. Proto se může velmi dobře stát, že přechod na mikroslužby nemusí být úspěšný, protože na různé aplikace jsou kladeny dosti rozdílné požadavky. Taktéž je nutné upozornit na to, že nasazování a provozování mikroslužeb vyžaduje odlišný přístup, než je tomu u monolitických aplikací. Stručně řečeno: mikroslužby nejsou řešením pro každou situaci a na každý problém!

Přechod na mikroslužby navíc vyžaduje i změnu v myšlení architektů a vývojářů. U monolitické aplikace je vše, zejména změny stavu, v podstatě jednoduché a předvídatelné (ostatně je to důsledek vývoje, který trval přibližně sedm desetiletí) – volání modulů dává zaručené výsledky, databáze typicky podporuje všechny čtyři vlastnosti ACID atd. U mikroslužeb je naproti tomu nutné brát do úvahy zejména problematické vlastnosti počítačové sítě (viz též třináctou kapitolu v předchozím článku) a taktéž důsledky použití několika izolovaných databází.

Poznámka: i přesto, že se někdy používají dosti sofistikované technologie typu SOAP, CORBA, RPC atd., nejsme nikdy od vlastností počítačové sítě dokonale odstínění.

Velmi pěkně jsou některé vlastnosti (počítačových) sítí shrnuty ve článku Standing on Distributed Shoulders of Giants: Farsighted Physicists of Yore Were Danged Smart!.

3. Distribuovaná datová úložiště

V předchozím článku s úvodními informacemi o mikroslužbách jsme si mj. řekli, že by jednotlivé služby mezi sebou neměly sdílet společnou databázi. Tento požadavek je vlastně pochopitelný, protože se snažíme služby od sebe oddělit, a to jak z hlediska architektury celé aplikace, tak i z pohledu vývojářů – prakticky totiž mohou jednotlivé mikroslužby vyvíjet oddělené týmy, které se musí domluvit na API, ovšem nikoli již na interních záležitostech a logice. A právě sem spadají i databáze, popř. obecněji řečeno datová úložiště – mnoho tabulek a jejich sloupců (pokud se budeme držet relačních databází) nesouvisí s API služby a s její požadovanou vnější funkcionalitou. Na druhou stranu i relativně malá změna v databázovém schématu může rozbít všechny služby, které tuto databázi společně používají.

Přístup k databázím se ve světě mikroslužeb skutečně dosti podstatným způsobem odlišuje od světa monolitických aplikací, a již na začátku je nutné upozornit na to, že mnohé vlastnosti databází, která běžně využíváme (ACID, …) nebudou u mikroslužeb „automagicky“ splněny a mnohdy se programátoři musí sami postarat o to, aby byla příslušná vlastnost databáze skutečně dodržena (pochopitelně jen za předpokladu, že je to skutečně zapotřebí). Na druhou stranu se nám však otevírá celý nový prostor pro nová řešení založená například na perzistentním logu s událostmi atd.

Obrázek 1: Jednotlivé mikroslužby mezi sebou mohou komunikovat například s využitím protokolu HTTP (REST API), STOMP atd. Ovšem důležité je, že každá mikroslužba má svoji databázi.

4. Příklad databáze v monolitické aplikaci s implementací e-shopu

Nejprve si ovšem ve stručnosti ukažme, jakým způsobem může být řešena databáze v monolitické aplikaci při implementaci jednoduchého e-shopu. Soustředit se budeme pouze na její jedinou část – vyřizování objednávek provedených jednotlivými přihlášenými uživateli (u nichž budeme při vyřizování objednávky znát jejich ID).

Obrázek 2: Služba v reálném provozu při použití HTTP serveru, k němuž se připojují klienti.

V tom nejjednodušším případě uvažujme o tom, že databáze bude obsahovat pouhé dvě tabulky nazvané ORDERS a CUSTOMERS. Ve skutečnosti se ovšem pochopitelně bude muset pracovat s více tabulkami, například seznamem zboží pro daný ORDERS.ID, historií nákupů pro CUSTOMERS.ID atd. Tyto tabulky mohou (při značném zjednodušení) vypadat například následovně.

Tabulka CUSTOMERS obsahuje základní informace o zákaznících, přičemž si u každého zákazníka pamatuje i nejvyšší sumu, za kterou tento zákazník může vytvořit objednávku, popř. více objednávek. Business logika aplikace musí zaručit, že tato suma nebude překročena:

CREATE TABLE CUSTOMERS(
    ID            INT NOT NULL,
    NAME          VARCHAR (20) NOT NULL,
    SURNAME       VARCHAR (30) NOT NULL,
    CREDIT_LIMIT  DECIMAL(8,2),
    ...
    ...
    ...
    PRIMARY KEY (ID)
);

Tabulka s objednávkami obsahuje seznam všech objednávek, přičemž každá objednávka je na nějakou sumu a navíc je vztažena ke konkrétnímu zákazníkovi. Další sloupce obsahují časové razítko vytvoření objednávky a popř. i její stav:

CREATE TABLE ORDERS(
    ID            INT NOT NULL,
    DATE          DATETIME,
    CUSTOMER_ID   INT NOT NULL references CUSTOMERS(ID),
    TOTAL         DECIMAL(8,2),
    STATE         CHAR(1),
    ...
    ...
    ...
    PRIMARY KEY (ID)
);
Poznámka: příklad, který si zde popisujeme, je do značné míry podobný příkladu, jenž poměrně často zmiňuje Chris Richardson ve svých přednáškách. Jako u každého umělého příkladu se samozřejmě jedná o značné zjednodušení, které sice dobře ukazuje některé vlastnosti monolitických aplikací i mikroslužeb, ovšem kvůli malé složitosti se zde příliš neukazuje, z jakého důvodu začaly být mikroslužby v některých oborech IT tak populární.

Při objednávce zboží je možné nad operacemi v databázi provádět všechny potřebné kontroly, a to většinou naprosto triviálním způsobem. Například můžeme zaručit, že se objednávka podaří jen ve chvíli, kdy bude platit podmínka:

sum(ORDERS.TOTAL) <= CUSTOMER.CREDIT_LIMIT
Poznámka: samozřejmě je nutné podmínku splnit s příslušnými vazbami přes CUSTOMERS.ID atd.

5. Některé užitečné vlastnosti databáze použité v monolitické aplikaci

To pochopitelně není vše, protože pokud celou operaci objednávky uzavřeme do transakce, zaručí výše zmíněná kontrola automatický rollback celé transakce v případě, že se zákazník bude snažit objednat zboží, jehož celková cena přesahuje jeho maximální povolený limit. Jak kontrolu, tak i transakci lze přitom v monolitické aplikaci s jedinou databází naprogramovat doslova na několika řádcích programového kódu. Dále je možné velmi snadno zaručit automatickou serializaci transakcí pro jednoho zákazníka, tj. vyloučí se například ty situace, kdy se nějaký zákazník bude snažit provést dva nákupy současně, přičemž cena každého nákupu zvlášť se sice vejde do limitní ceny, ale oba nákupy již finanční možnosti zákazníka převyšují. A nakonec – opět díky transakcím – je stav databáze vždy konzistentní, protože se objednávka buď provede (s odečtením kreditu) nebo neprovede, což se například projeví úpravou ORDERS.STATUS na hodnotu „denied“. Nebude zde nutné řešit speciální stavy typu „pending“ atd.

I z hlediska logiky samotné aplikace se jedná o triviální úkol: uživatel stiskne tlačítko Order, aplikace se pokusí provést transakci a pokud transakce proběhla, je o tom uživatel prakticky okamžitě informován, podobně jako bude informován v případě zamítnutí objednávky. Celý kód je proveden synchronně (s případnou výjimkou, která se týká webového UI, ovšem předpokládejme, že zákazník bude moci použít i synchronní API).

6. Databáze v aplikaci postavené na mikroslužbách

V případě, že budeme navrhovat aplikaci s e-shopem postavenou na mikroslužbách, může vést tento návrh k tomu, že se vytvoří (kromě dalších modulů) i mikroslužby nazvané například customer service a order service. Každá z těchto mikroslužeb bude mít i vlastní databázi. Konkrétně v případě první služby bude tato databáze obsahovat tabulku nazvanou ORDERS (plus případné další potřebné tabulky) a druhá služba pak bude obsahovat tabulku CUSTOMERS. Již zde ovšem nebude možné nadeklarovat referenční integritu, tj. definovat cizí klíč v podřízené tabulce ORDERS:

CUSTOMER_ID   INT NOT NULL references CUSTOMERS(ID),

Samotné „rozseknutí“ databáze na dvě samostatné databáze však navíc vede k tomu, že ztratíme všechny vlastnosti ACIDu poskytované vlastním databázovým systémem a pokud ACID (či některou z jeho čtyř vlastností) budeme potřebovat, musíme ho nějakým způsobem reimplementovat.

Obrázek 3: Mikroslužby customer service a order service, každá se svou vlastní databází.

7. Dvoufázový commit v mikroslužbách?

V klasických distribuovaných systémech se pro tento účel používá dvoufázový commit neboli two-phase commit, zkráceně též 2PC. Teoreticky je pochopitelně možné dvoufázový commit použít i u aplikace založené na mikroslužbách, ale tento koncept se v praxi příliš nedoporučuje používat, neboť se tím zvyšuje počet zpráv posílaných mezi jednotlivými mikroslužbami, služby jsou na úrovni databází propojené do větší míry, než je většinou akceptovatelné a navíc se tím ztrácí i požadavek na to, aby byla aplikace funkční i při (krátkodobém či střednědobém) výpadku jednotlivých uzlů; zde konkrétně dojde k problému při výpadku uzlu, který celou transakci musí koordinovat. Kromě toho vyžaduje dvoufázový commit předání několika zpráv (minimálně čtyř), což může být u vysoce zatížených aplikací příliš mnoho. A navíc, což je praktičtější problém: mnohé NoSQL databáze, brokery atd. koncept dvoufázového commitu prozatím nepodporují.

8. Další možnost: kompenzační transakce

Jedna z náhrad dvoufázových commitů spočívá ve využití takzvaných kompenzačních transakcí (což s velkou pravděpodobností není ustálený český ekvivalent termínu compensating transaction). Princip je vlastně velmi jednoduchý (o to komplikovanější bývá jeho nasazení v praxi): v každé databázi se provádí změny v transakcích, ovšem pro každou takovou transakci musí existovat i opačná operace, která původní transakci vyruší. Tato kompenzační transakce je typicky vyvolána jinou službou. V našem konkrétním případě by jedna z transakcí spočívala ve vyřízení objednávky pro daného uživatele CUSTOMER.ID ve službě order service. V tento okamžik by byla (například přes orchestraci nebo choreografii) informována služba customer service, aby snížila kredit pro zákazníka. Ovšem pokud kredit klesne na nulu, bude nutné objednávku zrušit – a právě to je úkol pro kompenzační transakci.

Obrázek 4: Transakce a kompenzační transakce. Každá transakce je provedena jinou mikroslužbou, ovšem pokud dojde ke vzniku chyby (malý kredit atd.) je vyvolána zpětná transakce. Na tomto obrázku jsou běžné transakce nazvány Tx, zpětné transakce Cx.

Poznámka: situace je samozřejmě ještě složitější ve chvíli, kdy se zákazník pokusí provést více objednávek.

9. Použití nástrojů pro streaming: záznam a přehrávání sekvence událostí

Připomeňme si, že jedním ze základních konceptů, na nichž jsou mikroslužby založeny, je použití vzájemně izolovaných databází pro každou mikroslužbu, což je jeden z přístupů umožňujících škálování jak samotné aplikace, tak i vývojových týmů. Pochopitelně je však nutné nějakým způsobem zajistit, aby se změny provedené v jedné databázi (změna limitu kreditu u zákazníka) nějakým způsobem promítly do další služby. Jedním ze způsobů, jak to zařídit (a to navíc poměrně elegantním způsobem) je využití nástrojů pro streaming. Typicky se jedná o systém Apache Kafka, popř. na Rootu popsaný nástroj NATS Streaming Server. Tyto nástroje umožňují zaznamenávat a později přehrávat (replay) záznamy s informacemi o událostech, k nimž v aplikaci došlo. Na tyto události mohou reagovat další mikroslužby a případně na jejich základu provádět změny ve vlastní databázi.

10. Koncepty, na nichž je streaming založen

Streaming je založen na příjímaní a ukládání zpráv se zaručeným pořadím. Po příjmu je každé zprávě přidělen jednoznačný celočíselný offset (a většinou i časové razítko). To, že každá zpráva uložená do oddílu má přiřazen offset, umožňuje, aby konzumenti zpráv specifikovali, od jakého offsetu potřebují zprávy přijímat. Díky tomu je možné, aby se konzumenti připojovali a odpojovali v jakýkoli okamžik a přitom měli možnost řídit, s jakými zprávami budou pracovat. Konzument se například po pozdějším připojení může rozhodnout, že bude zpracovávat nejnovější data a na případná starší data si (možná) vyhradí svůj strojový čas později. Umožněn je i takzvaný replay, tj. zpracování určité sekvence zpráv z minulosti. Postačuje znát jen offset první zprávy ze sekvence.

Samotné posílání zpráv konzumentů si přitom řídí sami konzumenti, kteří si zprávy (téma+oddíl+offset) musí vyžádat. Díky tomu si sami konzumenti určují, kolik zpráv zpracují a jak tedy budou zatíženi.

To však ve skutečnosti není vše, protože zprávy poslané do message brokera s podporou streamingu se v čase chovají odlišně, než v případě použití běžné fronty. Zprávy jsou totiž rozšířeny o již výše zmíněné pořadové číslo a většinou i o časové razítko. Takto rozšířené zprávy jsou uloženy do takzvaného logu s daným tématem, kde jsou uchovány buď po neomezeně dlouhou dobu, nebo až do chvíle, kdy je překročena kapacita logu, popř. maximální doba životnosti zprávy. Pokud dojde k alespoň jedné této události, budou nejstarší zprávy smazány (kapacita logu je většinou zadána jak celkovým objemem, tak i maximálním počtem zpráv, což je ostatně i případ NATS Streaming Serveru). Nezáleží tedy na tom, zda a kolika konzumenty byla zpráva přečtena a zpracována – přečtení a zpracování zprávy nemá vliv na její umístění v logu, což vlastně znamená, že většinou dochází k určitému zesložitění konzumentů, kteří si musí pamatovat, které zprávy s daným tématem již zpracovaly a které nikoli.

Aby bylo možné konfigurovat a řídit, které zprávy mají být na message brokeru uloženy a které již smazány, specifikuje se takzvaný retention time zajišťující, aby počet zpráv/záznamů nepřekročil časovou mez. Mnohé streaming servery dokážou omezit i celkový počet zpráv, počet zpráv v tématu a/nebo počet zpráv na jednom serveru v clusteru. Totéž omezení je možné aplikovat na celkovou velikost použitého paměťového či diskového prostoru.

Dále se u streaming serverů setkáme s možností zapojení více serverů do clusteru, což podporuje jak dnes popisovaný NATS Streaming Server, tak i Apache Kafka.

11. Nejznámější systémy podporující streaming

Mezi nejznámější a pravděpodobně nejčastěji nasazované systémy podporující streaming patří projekt Apache Kafka a taktéž již dříve popsaný NATS Streaming Server. Použít je možné i službu AWS Kinesis, ovšem Kinesis se většinou používá v jiném kontextu (tímto nástrojem se budeme zabývat v samostatném článku). Nejznámější je ovšem první zmíněný projekt – Apache Kafka.

Obrázek 5: Logo systému Kafka.

Obrázek 6: Logo systému NATS.

Mezi další systémy, tentokrát určené pro záznam neměnitelných logů (událostí), patří například:

  1. Apache Pulsar
  2. Azure Cosmos DB Change Feed
  3. Azure EventHub
  4. DistributedLog
  5. Chronicle Queue
  6. Pravega

12. Apache Kafka

Apache Kafka umožňuje ukládání zpráv (zde se ovšem používá termín záznam – record) do různých témat, přičemž každé téma je rozděleno do oddílů neboli partition (samozřejmě je možné pro téma vyhradit pouze jediný oddíl). Rozdělení do oddílů se provádí z několika důvodů. Jedním z nich je rozdělení zátěže, protože jednotlivé oddíly mohou být provozovány na různých počítačích v clusteru. To však není vše, jelikož je ve skutečnosti konfigurace poněkud složitější – každý oddíl totiž může být replikován na více počítačích, přičemž jeden z těchto oddílů je „leaderem“ a ostatní jsou „followeři“. Zápis nových zpráv, popř. čtení se provádí vždy jen v leaderu, ovšem změny jsou replikovány na všechny kopie oddílu. Ve chvíli, kdy dojde k pádu „leadera“, převezme jeho roli jeden z dalších uzlů. Pokud tedy existuje N uzlů s replikou oddílu, bude systém funkční i ve chvíli, kdy zhavaruje N-1 uzlů!

Téma zpracovávané Kafkou může na clusteru vypadat například následovně:

          +---+---+---+---+---+---+
oddíl #0  | 0 | 1 | 2 | 3 | 4 | 5 | ...
          +---+---+---+---+---+---+
oddíl #1  | 0 | 1 | 2 | ...
          +---+---+---+
oddíl #2  | ...
          +---+---+---+---+---+---+---+---+---+
oddíl #3  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ...
          +---+---+---+---+---+---+---+---+---+

Boxy s čísly odpovídají jednotlivým zprávám, kterým jsou tato pořadová čísla v sekvenci postupně přiřazována. Zápis nových zpráv je prováděn do oblastí označených třemi tečkami. Z tohoto diagramu můžeme odvodit, že každý oddíl obsahuje vlastní sekvenci zpráv/záznamů, ke kterým se postupně připojují záznamy další.

Pozor si musíme dát především na to, že pořadí záznamů je zachováno a garantováno pouze v rámci jednoho oddílu a nikoli pro celé téma. V případě, že požadujeme, aby všechny zprávy v tématu měly zaručené pořadí, používá se pouze jeden oddíl pro celé téma, což má ovšem vliv na další vlastnosti řešení – především se snižuje výkonnost a možnost rozkládání zátěže v rámci clusteru.

Producent při posílání zprávy volí jak téma (což je logické), tak i oddíl. Volbu oddílu je možné provést na základě mnoha kritérií. Například lze použít jednoduchý algoritmus round robin a používat oddíly čistě pro rozložení zátěže. Nebo se může oddíl zvolit například na základě ID přihlášeného uživatele, poslední části IP počítače, na němž je spuštěn producent atd. atd.

Podobně jako u dalších message brokerů s podporou streamování se i v systému Apache Kafka o výběr zpráv, které se mají zpracovat, starají přímo konzumenti, kteří mohou specifikovat offset zprávy. Konzumenti se spojují do skupin, přičemž zprávu získá vždy jeden z konzumentů ze skupiny (což se podobá strategii pub-sub).

Apache Kafka podporuje i speciální API používané ve chvíli, kdy potřebujeme implementovat uzel (proces), který získá zprávu, nějakým způsobem ji zpracuje a poté ji pošle zpět do message brokera. Takový uzel je samozřejmě možné naimplementovat tak, že se bude současně jednat o konzumenta i producenta, ovšem využití speciálního API je efektivnější.

13. Nahrávání a přehrávání událostí při použití distribuovaných databází

Události je do streaming služby možné nahrávat ručně (resp. přesněji řečeno explicitně napsaným programovým kódem), nebo lze využít nástroje typu CDC (Change Data Capture), které dokážou změny stavu prováděné na úrovni databáze (INSERT, UPDATE, DELETE) detekovat a zpracovat automaticky. Mezi tyto nástroje patří zejména Debezium, kterému se budeme věnovat v samostatném článku. Výsledkem explicitně napsaného programového kódu nebo výsledkem práce Debezia je sekvence událostí reflektujících postupně prováděné změny stavu v databázi. Tyto události jsou posílány na streaming server (Kafka, NATS) a odtud si je může (přes mnoho již připravených konektorů) načítat libovolná další služba a adekvátně na ně reagovat (což může vést ke vzniku další události). Předností je tedy fakt, že služby mezi sebou nemají pevnou vazbu a služby, které na události reagují, vůbec nemusí v daný okamžik běžet. K záznamům se totiž lze vracet; jak dlouho do minulosti již záleží na nastavení streaming služby (celkový počet záznamů, časové razítko, obsazení diskového prostoru atd. atd.)

Obrázek 7: Detekce změny v databázích s uložením události do Apache Kafky.

Poznámka: mezi změnou záznamu/záznamů v databázi a vytvořením události je podstatný rozdíl. Zatímco změna v databází mění globální stav aplikace (imperative state mutation) závislý na čase (předtím byl stav jiný, později může být opět jiný), vytvoření události je informací o neoddiskutovatelném faktu, který nastal v přesně specifikovaném okamžiku (immutable event/fact). Přehráváním událostí se tedy pohybujeme mezi sérií neměnitelných stavů.

Obrázek 8: Reakce jednotlivých služeb na události. Povšimněte si, že každá služba se může nacházet v jiném stavu zpracování (na jiné události, resp. na jiném offsetu).

14. Architektura kappa

„Databases are global, shared, mutable state. That’s the way it has been since the 1960s, and no amount of NoSQL has changed that. However, most self-respecting developers have got rid of mutable global variables in their code long ago. So why do we tolerate databases as they are?“

Existuje ovšem ještě jedno zajímavé řešení problematiky databází v architekturách založených na mikroslužbách. Toto řešení se někdy nazývá architektura kappa, což je označení používané pro odlišení od známé architektury lambda používané ve streamingu. Toto řešení je založeno na tom, že primárním zdrojem informací o stavu aplikace nejsou samotné SQL či NoSQL databáze, ale přímo události zaznamenané do nástroje pro streaming (typicky se zde používá Apache Kafka). Ovšem i databáze se v této architektuře používají, a to konkrétně ve funkci materializovaných pohledů (materialized views). Každá mikroslužba, která vlastní databázi, postupně přijímá jednotlivé události (změna kreditu u zákazníka, objednávka X kusů zboží Y) a mění obsah své databáze na základě těchto údajů (změna kreditu je UPDATE s přímou změnou hodnoty, objednávka X kusů zboží Y může v mikroslužbě skladu snížit počet dostupných kusů Y o hodnotu X atd. atd.). Takové databáze kvůli nutným zpožděním nebudou obsahovat skutečný stav aplikace, ale „současný stav“, kde je ve skutečnosti ona „současnost“ posunuta oproti reálnému času do minulosti.

15. Příklad architektury kappa

Na devátém obrázku je ukázán příklad jednoduché aplikace používající architekturu kappa. Povšimněte si zejména barevných šipek naznačujících tok dat:

ict ve školství 24

  1. Červené šipky představují změnu stavu aplikace; informace o této změně je poslána do Apache Kafky či do systému NATS.
  2. Obsah jednotlivých databází je postupně updatován (modré šipky).
  3. Databáze přitom slouží jako materializované pohledy pro jednotlivé mikroslužby (zelené šipky).

Obrázek 9: Příklad aplikace používající architekturu kappa.

16. Přednosti architektury kappa

Výše popsaná architektura kappa nabízí až překvapivě velké množství předností, zejména pak:

  1. Mikroslužby jsou od sebe izolovány, není zapotřebí používat orchestraci atd.
  2. Pokud se do aplikace přidá nová mikroslužba, naplní se její databáze jednoduše – přehráním všech událostí zaznamenaných v nástroji pro streaming.
  3. Krátkodobý výpadek nějaké služby nevede ke ztrátě dat, ovšem může vést k viditelnému zpomalení činnosti z pohledu uživatele (potvrzení operace atd. atd.)
  4. V případě, že je zapotřebí změnit schéma databáze v nějaké mikroslužbě, opět se může její znovunaplnění provést přehráním událostí (čemuž pravděpodobně budeme věřit více, než jednoúčelovému skriptu pro migraci databáze).
  5. Migrace na zcela jinou databázi (změna dodavatele, přechod SQL→NoSQL či naopak) se opět může provést přehráním událostí.
  6. A nakonec – celá aplikace získá prakticky zadarmo audit log (audit trail), samozřejmě za předpokladu, že události obsahují všechny důležité informace.
Poznámka: otázkou ovšem zůstávají nároky na potřebný diskový prostor, zejména ve chvíli, kdy je nastavena několikanásobná replikace celého logu s událostmi (a tuto replikaci samozřejmě vyžadujeme kvůli tomu, aby aplikace běžela i při výpadku N-1 streaming serverů z dostupného počtu N).

17. Odkazy na Internetu

  1. Microservices – Not a free lunch!
    http://highscalability.com/blog/2014/4/8/mi­croservices-not-a-free-lunch.html
  2. Microservices, Monoliths, and NoOps
    http://blog.arungupta.me/microservices-monoliths-noops/
  3. Microservice Design Patterns
    http://blog.arungupta.me/microservice-design-patterns/
  4. Vision of a microservice revolution
    https://www.jolie-lang.org/vision.html
  5. Microservices: a definition of this new architectural term
    https://martinfowler.com/ar­ticles/microservices.html
  6. Mikroslužby
    http://voho.eu/wiki/mikrosluzba/
  7. Microservice Prerequisites
    https://martinfowler.com/bli­ki/MicroservicePrerequisi­tes.html
  8. Microservices in Practice, Part 1: Reality Check and Service Design (vyžaduje registraci)
    https://ieeexplore.ieee.or­g/document/7819415
  9. Microservice Trade-Offs
    https://www.martinfowler.com/ar­ticles/microservice-trade-offs.html
  10. What is a microservice? (from a linguistic point of view)
    http://claudioguidi.blogspot­.com/2017/03/what-microservice-from-linguisitc.html
  11. Microservices (Wikipedia)
    https://en.wikipedia.org/wi­ki/Microservices
  12. Fallacies of distributed computing (Wikipedia)
    https://en.wikipedia.org/wi­ki/Fallacies_of_distributed_com­puting
  13. Service (systems architecture)
    https://en.wikipedia.org/wi­ki/Service_(systems_archi­tecture)
  14. Microservices in a Nutshell
    https://www.thoughtworks.com/in­sights/blog/microservices-nutshell
  15. What is Microservices?
    https://smartbear.com/solu­tions/microservices/
  16. Mastering Chaos – A Netflix Guide to Microservices
    https://www.youtube.com/wat­ch?v=CZ3wIuvmHeM&t=17s
  17. Messaging in Microservice Architecture
    https://www.youtube.com/wat­ch?v=MkQWQ5f-SEY
  18. Pattern: Messaging
    https://microservices.io/pat­terns/communication-style/messaging.html
  19. Microservices Messaging: Why REST Isn’t Always the Best Choice
    https://blog.codeship.com/mi­croservices-messaging-rest-isnt-always-best-choice/
  20. Protocol buffers
    https://developers.google.com/protocol-buffers/
  21. BSON
    http://bsonspec.org/
  22. Apache Avro!
    https://avro.apache.org/
  23. REST vs Messaging for Microservices – Which One is Best?
    https://solace.com/blog/experience-awesomeness-event-driven-microservices/
  24. How did we end up here?
    https://gotocon.com/dl/goto-chicago-2015/slides/MartinThompson_an­d_ToddMontgomery_HowDidWe­EndUpHere.pdf
  25. Scaling microservices with message queues to handle data bursts
    https://read.acloud.guru/scaling-microservices-with-message-queue-2d389be5b139
  26. Microservices: What are smart endpoints and dumb pipes?
    https://stackoverflow.com/qu­estions/26616962/microser­vices-what-are-smart-endpoints-and-dumb-pipes
  27. Common Object Request Broker Architecture
    https://en.wikipedia.org/wi­ki/Common_Object_Request_Bro­ker_Architecture
  28. Enterprise service bus
    https://en.wikipedia.org/wi­ki/Enterprise_service_bus
  29. Microservices vs SOA : What’s the Difference
    https://www.edureka.co/blog/mi­croservices-vs-soa/
  30. Pravda o SOA
    https://businessworld.cz/reseni-a-realizace/pravda-o-soa-2980
  31. Is it a good idea for Microservices to share a common database?
    https://www.quora.com/Is-it-a-good-idea-for-Microservices-to-share-a-common-database
  32. Pattern: Shared database
    https://microservices.io/pat­terns/data/shared-database.html
  33. Is a Shared Database in Microservices Actually an Anti-pattern?
    https://hackernoon.com/is-shared-database-in-microservices-actually-anti-pattern-8cc2536adfe4
  34. Shared database in microservices is a problem, yep
    https://ayende.com/blog/186914-A/shared-database-in-microservices-is-a-problem-yep
  35. Microservices with shared database? using multiple ORM's?
    https://stackoverflow.com/qu­estions/43612866/microser­vices-with-shared-database-using-multiple-orms
  36. Examples of microservice architecture
    https://www.coursera.org/lecture/intro-ibm-microservices/examples-of-microservice-architecture-JXOFj
  37. Microservices: The Rise Of Kafka
    https://movio.co/blog/microservices-rise-kafka/
  38. Building a Microservices Ecosystem with Kafka Streams and KSQL
    https://www.confluent.io/blog/building-a-microservices-ecosystem-with-kafka-streams-and-ksql/
  39. An introduction to Apache Kafka and microservices communication
    https://medium.com/@ulymarins/an-introduction-to-apache-kafka-and-microservices-communication-bf0a0966d63
  40. ACID (computer science)
    https://en.wikipedia.org/wi­ki/ACID_(computer_science)
  41. Distributed transaction
    https://en.wikipedia.org/wi­ki/Distributed_transaction
  42. Two-phase commit protocol
    https://en.wikipedia.org/wiki/Two-phase_commit_protocol
  43. Why is 2-phase commit not suitable for a microservices architecture?
    https://stackoverflow.com/qu­estions/55249656/why-is-2-phase-commit-not-suitable-for-a-microservices-architecture
  44. 4 reasons why microservices resonate
    https://www.oreilly.com/ideas/4-reasons-why-microservices-resonate
  45. Pattern: Microservice Architecture
    https://microservices.io/pat­terns/microservices.html
  46. Pattern: Monolithic Architecture
    https://microservices.io/pat­terns/monolithic.html
  47. Pattern: Saga
    https://microservices.io/pat­terns/data/saga.html
  48. Pattern: Database per service
    https://microservices.io/pat­terns/data/database-per-service.html
  49. Pattern: Access token
    https://microservices.io/pat­terns/security/access-token.html
  50. Databázová integrita
    https://cs.wikipedia.org/wi­ki/Datab%C3%A1zov%C3%A1_in­tegrita
  51. Referenční integrita
    https://cs.wikipedia.org/wi­ki/Referen%C4%8Dn%C3%AD_in­tegrita
  52. Introduction into Microservices
    https://specify.io/concep­ts/microservices
  53. Are Microservices ‘SOA Done Right’?
    https://intellyx.com/2015/07/20/are-microservices-soa-done-right/
  54. The Hardest Part About Microservices: Your Data
    https://blog.christianpos­ta.com/microservices/the-hardest-part-about-microservices-data/
  55. From a monolith to microservices + REST
    https://www.slideshare.net/InfoQ/from-a-monolith-to-microservices-rest-the-evolution-of-linkedins-service-architecture
  56. DevOps and the Myth of Efficiency, Part I
    https://blog.christianpos­ta.com/devops/devops-and-the-myth-of-efficiency-part-i/
  57. DevOps and the Myth of Efficiency, Part II
    https://blog.christianpos­ta.com/devops/devops-and-the-myth-of-efficiency-part-ii/
  58. Standing on Distributed Shoulders of Giants: Farsighted Physicists of Yore Were Danged Smart!
    https://queue.acm.org/deta­il.cfm?id=2953944
  59. Building DistributedLog: High-performance replicated log service
    https://blog.twitter.com/en­gineering/en_us/topics/in­frastructure/2015/building-distributedlog-twitter-s-high-performance-replicated-log-servic.html
  60. Turning the database inside-out with Apache Samza
    https://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/
  61. Debezium: Stream changes from your databases.
    https://debezium.io/
  62. Change data capture
    https://en.wikipedia.org/wi­ki/Change_data_capture
  63. Apache Samza (Wikipedia)
    https://en.wikipedia.org/wi­ki/Apache_Samza
  64. Storm (event processor)
    https://en.wikipedia.org/wi­ki/Storm_(event_processor)
  65. kappa-architecture.com
    http://milinda.pathirage.org/kappa-architecture.com/
  66. Questioning the Lambda Architecture
    https://www.oreilly.com/i­deas/questioning-the-lambda-architecture
  67. Lambda architecture
    https://en.wikipedia.org/wi­ki/Lambda_architecture
  68. Event stream processing
    https://en.wikipedia.org/wi­ki/Event_stream_processing
  69. How to beat the CAP theorem
    http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html
  70. Kappa Architecture Our Experience
    https://events.static.linux­found.org/sites/events/fi­les/slides/ASPgems%20-%20Kappa%20Architecture.pdf

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.