Megosztás a következőn keresztül:


Az elosztott adatkezelés kihívásai és megoldásai

Tipp.

Ez a tartalom egy részlet a .NET-alkalmazásokhoz készült .NET-alkalmazásokhoz készült eBook, .NET Microservices Architecture című eBookból, amely elérhető a .NET Docs-on vagy egy ingyenesen letölthető PDF-fájlként, amely offline módban is olvasható.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

1. kihívás: Az egyes mikroszolgáltatások határainak meghatározása

A mikroszolgáltatások határainak meghatározása valószínűleg az első kihívás, amivel bárki szembesül. Minden mikroszolgáltatásnak az alkalmazás egy darabjának kell lennie, és minden mikroszolgáltatásnak autonómnak kell lennie az általa nyújtott összes előnnyel és kihívással. De hogyan azonosítja ezeket a határokat?

Először az alkalmazás logikai tartománymodelljeire és a kapcsolódó adatokra kell összpontosítania. Próbálja meg azonos alkalmazáson belül azonosítani a leválasztott adatszigeteket és a különböző környezeteket. Az egyes környezetek eltérő üzleti nyelvvel (eltérő üzleti feltételekkel) rendelkezhetnek. A környezeteket egymástól függetlenül kell meghatározni és felügyelni. A különböző kontextusokban használt kifejezések és entitások hasonlónak tűnhetnek, de előfordulhat, hogy egy adott kontextusban egy üzleti koncepciót más célra használnak egy másik kontextusban, és akár más néven is lehetnek. A felhasználót például nevezhetjük felhasználónak az identitás- vagy tagsági környezetben, ügyfélként a CRM-környezetben, vevőként egy rendelési környezetben stb.

Az egyes környezetekhez tartozó különböző tartományokkal rendelkező alkalmazáskörnyezetek közötti határokat pontosan így azonosíthatja az egyes üzleti mikroszolgáltatások és azok kapcsolódó tartománymodellje és adatai számára. Mindig megpróbálja minimalizálni a mikroszolgáltatások közötti kapcsolatot. Ez az útmutató részletesebben ismerteti ezt az azonosítási és tartománymodell-kialakítást az egyes mikroszolgáltatások tartománymodell-határainak azonosítása című szakaszában.

2. kihívás: Olyan lekérdezések létrehozása, amelyek adatokat kérnek le több mikroszolgáltatásból

A második kihívás az, hogy hogyan lehet olyan lekérdezéseket implementálni, amelyek adatokat kérnek le több mikroszolgáltatásból, miközben elkerülik a távoli ügyfélalkalmazások mikroszolgáltatásai felé irányuló csevegést. Ilyen lehet például egy mobilalkalmazás egyetlen képernyője, amely a kosár, a katalógus és a felhasználói identitás mikroszolgáltatásai által birtokolt felhasználói adatokat jeleníti meg. Egy másik példa egy összetett jelentés, amely több mikroszolgáltatásban található táblát foglal magában. A megfelelő megoldás a lekérdezések összetettségétől függ. Mindenesetre szüksége lesz egy módszerre az információk összesítésére, ha javítani szeretné a rendszer kommunikációjának hatékonyságát. A legnépszerűbb megoldások a következők.

API-átjáró. Több különböző adatbázissal rendelkező mikroszolgáltatás egyszerű adatösszesítéséhez az ajánlott módszer az API Gatewaynek nevezett összesítési mikroszolgáltatás. Azonban óvatosnak kell lennie a minta implementálásával kapcsolatban, mivel ez a rendszer fojtópontja lehet, és megsértheti a mikroszolgáltatás-autonómia elvét. Ennek a lehetőségnek a mérséklése érdekében több finomított API Gateway is rendelkezhet, amelyek mindegyike a rendszer függőleges "szeletére" vagy üzleti területére összpontosít. Az API Gateway-minta részletesebb ismertetését az API Gateway szakasz későbbi részében találja.

A GraphQL Összevonás egy lehetőség, amelyet figyelembe lehet venni, ha a mikroszolgáltatások már használják a GraphQL-t, az a GraphQL-összevonás. Az összevonás lehetővé teszi, hogy más szolgáltatásokból származó "algráfokat" definiáljon, és azokat egy önálló sémaként működő összesített "szupergráfba" írja.

CQRS lekérdezési/olvasási táblákkal. Egy másik megoldás az adatok több mikroszolgáltatásból való összesítésére a Materialized View minta. Ebben a megközelítésben előre létrehoz (denormalizált adatokat készít a tényleges lekérdezések előtt), egy írásvédett táblát, amely több mikroszolgáltatás tulajdonában lévő adatokat tartalmaz. A táblázat formátuma megfelel az ügyfélalkalmazás igényeinek.

Fontolja meg egy mobilalkalmazás képernyőjét. Ha egyetlen adatbázissal rendelkezik, előfordulhat, hogy a képernyő adatait egy olyan SQL-lekérdezés használatával kéri össze, amely több táblát érintő összetett illesztéseket hajt végre. Ha azonban több adatbázissal rendelkezik, és mindegyik adatbázis egy másik mikroszolgáltatás tulajdonában van, nem kérdezheti le ezeket az adatbázisokat, és nem hozhat létre SQL-illesztéseket. Az összetett lekérdezés kihívássá válik. A követelményt CQRS-megközelítéssel oldhatja meg – denormalizált táblát hozhat létre egy másik adatbázisban, amelyet csak lekérdezésekhez használnak. A tábla kifejezetten az összetett lekérdezéshez szükséges adatokhoz tervezhető, az alkalmazás képernyőjéhez szükséges mezők és a lekérdezési tábla oszlopai közötti egy-az-egyhez kapcsolattal. Jelentéskészítési célokra is szolgálhat.

Ez a megközelítés nem csak az eredeti problémát oldja meg (a mikroszolgáltatások közötti lekérdezést és csatlakozást), hanem jelentősen javítja a teljesítményt egy összetett illesztéshez képest, mivel már rendelkezik az alkalmazás által igényelt adatokkal a lekérdezési táblában. A command and Query Responsibility Szegregáció (CQRS) lekérdezési/olvasási táblákkal való használata természetesen további fejlesztési munkát jelent, és a végleges konzisztenciát is át kell vennie. Ennek ellenére a teljesítményre és a nagy skálázhatóságra vonatkozó követelmények az együttműködésen alapuló forgatókönyvekben (vagy a versenyhelyzetekben, a nézőponttól függően) a CQRS több adatbázissal való alkalmazásához szükségesek.

"Hideg adatok" a központi adatbázisokban. Olyan összetett jelentések és lekérdezések esetében, amelyek nem igényelnek valós idejű adatokat, gyakori módszer a "gyakori adatok" (a mikroszolgáltatásokból származó tranzakciós adatok) "hideg adatként" való exportálása nagy adatbázisokba, amelyek csak jelentéskészítésre használhatók. Ez a központi adatbázisrendszer lehet big data-alapú rendszer, például Hadoop; az Azure SQL Data Warehouse-on alapuló adattárházhoz hasonló adattárház; vagy akár egyetlen SQL-adatbázist is, amelyet csak jelentésekhez használnak (ha a méret nem jelent problémát).

Ne feledje, hogy ez a központosított adatbázis csak olyan lekérdezésekhez és jelentésekhez használható, amelyekhez nincs szükség valós idejű adatokra. Az eredeti frissítéseknek és tranzakcióknak, mint az igazság forrása, a mikroszolgáltatási adatokban kell lenniük. Az adatok szinkronizálásának módja eseményvezérelt kommunikációval (a következő szakaszokban) vagy más adatbázis-infrastruktúra importálási/exportálási eszközeivel történik. Ha eseményvezérelt kommunikációt használ, az integrációs folyamat hasonló lenne ahhoz, ahogyan a CQRS-lekérdezéstáblák esetében korábban leírt módon propagálja az adatokat.

Ha azonban az alkalmazástervezés során több mikroszolgáltatás információinak folyamatosan összesítése történik összetett lekérdezésekhez, az a rossz tervezés egyik tünete lehet – a mikroszolgáltatásnak a lehető legelkülönebbre kell elkülönítenie a többi mikroszolgáltatástól. (Ez kizárja azokat a jelentéseket/elemzéseket, amelyeknek mindig hidegadat-alapú központi adatbázisokat kell használniuk.) A probléma gyakori oka lehet a mikroszolgáltatások egyesítése. Ki kell egyensúlyoznia az egyes mikroszolgáltatások evolúciójának és üzembe helyezésének autonómiáját erős függőségekkel, kohézióval és adatösszesítéssel.

3. kihívás: Konzisztencia elérése több mikroszolgáltatásban

Ahogy korábban említettem, az egyes mikroszolgáltatások tulajdonában lévő adatok privátak az adott mikroszolgáltatáshoz, és csak a mikroszolgáltatási API használatával érhetők el. Ezért a bemutatott kihívás az, hogyan valósíthat meg teljes körű üzleti folyamatokat, miközben a konzisztenciát több mikroszolgáltatásban is megtarthatja.

A probléma elemzéséhez tekintsünk meg egy példát az eShopOnContainers referenciaalkalmazásból. A katalógus mikroszolgáltatása minden termékre vonatkozóan fenntartja az információkat, beleértve a termék árát is. A Basket mikroszolgáltatás kezeli a felhasználók által a bevásárlókosaraikhoz hozzáadott termékelemek időbeli adatait, amely tartalmazza a termékek árát a kosárba helyezéskor. Amikor egy termék ára frissül a katalógusban, az árat is frissíteni kell az ugyanazon terméket tartalmazó aktív kosarakban, valamint a rendszernek valószínűleg figyelmeztetnie kell a felhasználót, hogy egy adott termék ára megváltozott, mivel hozzáadták a kosárhoz.

Az alkalmazás hipotetikus monolitikus verziójában, amikor az ár megváltozik a termékek táblájában, a katalógus alrendszere egyszerűen egy ACID-tranzakcióval frissítheti az aktuális árat a Basket táblában.

A mikroszolgáltatás-alapú alkalmazásokban azonban a termék- és kosártáblák a saját mikroszolgáltatásaik tulajdonában vannak. Egyetlen mikroszolgáltatás sem tartalmazhat egy másik mikroszolgáltatás tulajdonában lévő táblákat/tárolókat a saját tranzakcióiban, még a közvetlen lekérdezésekben sem, ahogy a 4–9. ábrán látható.

Diagram showing that microservices database data can't be shared.

4-9. ábra. Egy mikroszolgáltatás nem tud közvetlenül hozzáférni egy táblához egy másik mikroszolgáltatásban

A katalógus mikroszolgáltatásának nem szabad közvetlenül frissítenie a Basket táblát, mert a Basket tábla a Basket mikroszolgáltatás tulajdonában van. A Basket mikroszolgáltatás frissítéséhez a katalógus mikroszolgáltatásnak valószínűleg az aszinkron kommunikáción alapuló végleges konzisztenciát kell használnia, például integrációs eseményeken (üzeneten és eseményalapú kommunikáción) alapuló konzisztenciát. Az eShopOnContainers referenciaalkalmazás így végzi el ezt a fajta konzisztenciát a mikroszolgáltatásokban.

A CAP-tétel szerint választania kell a rendelkezésre állás és az ACID erős konzisztenciája között. A mikroszolgáltatás-alapú forgatókönyvek többsége a rendelkezésre állást és a magas méretezhetőséget követeli meg, szemben az erős konzisztenciával. A kritikus fontosságú alkalmazásoknak továbbra is működőképesnek kell lenniük, és a fejlesztők a gyenge vagy végleges konzisztenciával kapcsolatos technikákkal dolgozhatnak az erős konzisztencián. A legtöbb mikroszolgáltatás-alapú architektúra ezt a megközelítést alkalmazza.

Ezenkívül az ACID-stílusú vagy kétfázisú véglegesítési tranzakciók nem csupán a mikroszolgáltatás alapelveivel ütköznek; a legtöbb NoSQL-adatbázis (például az Azure Cosmos DB, a MongoDB stb.) nem támogatja a kétfázisú véglegesítési tranzakciókat, jellemzően elosztott adatbázisok esetében. A szolgáltatások és adatbázisok adatkonzisztenciájának fenntartása azonban elengedhetetlen. Ez a kihívás azzal a kérdéssel is összefügg, hogyan propagálja a módosításokat több mikroszolgáltatásban, ha bizonyos adatoknak redundánsnak kell lenniük – például ha a termék nevét vagy leírását a katalógus mikroszolgáltatásban és a Basket mikroszolgáltatásban kell megadnia.

Erre a problémára jó megoldás az eseményvezérelt kommunikáció és a közzétételi és előfizetési rendszer által tagolt mikroszolgáltatások végleges konzisztenciája. Ezeket a témaköröket az útmutató későbbi, Aszinkron eseményvezérelt kommunikáció című szakasza ismerteti.

4. kihívás: Kommunikáció tervezése mikroszolgáltatás-határokon

A mikroszolgáltatás-határokon keresztüli kommunikáció valódi kihívást jelent. Ebben az összefüggésben a kommunikáció nem utal arra, hogy milyen protokollt kell használnia (HTTP és REST, AMQP, üzenetkezelés stb.). Ehelyett azt határozza meg, hogy milyen kommunikációs stílust kell használnia, és különösen azt, hogy milyen legyen a mikroszolgáltatások összekapcsolása. Az összekapcsolás szintjétől függően a meghibásodás bekövetkezése esetén a hiba hatása jelentősen változik a rendszerre.

Egy olyan elosztott rendszerben, mint egy mikroszolgáltatás-alapú alkalmazás, olyan sok összetevő mozog, és az elosztott szolgáltatások számos kiszolgálón vagy gazdagépen keresztül futnak, az összetevők végül sikertelenek lesznek. Részleges meghibásodás és még nagyobb kimaradások lépnek fel, ezért meg kell terveznie a mikroszolgáltatásokat és a közöttük lévő kommunikációt, figyelembe véve az ilyen típusú elosztott rendszerek gyakori kockázatait.

Népszerű módszer a HTTP -alapú mikroszolgáltatások implementálása az egyszerűségük miatt. A HTTP-alapú megközelítés tökéletesen elfogadható; az itt található probléma a használat módjával kapcsolatos. Ha HTTP-kéréseket és válaszokat használ csak a mikroszolgáltatások ügyfélalkalmazásokból vagy API Gateway-átjárókból való használatához, az rendben van. Ha azonban a mikroszolgáltatások közötti szinkron HTTP-hívások hosszú láncait hozza létre, és úgy kommunikál a határokon, mintha a mikroszolgáltatások egy monolitikus alkalmazás objektumai lennének, az alkalmazás végül problémákba ütközik.

Tegyük fel például, hogy az ügyfélalkalmazás HTTP API-hívást indít egy egyéni mikroszolgáltatáshoz, például a Rendelési mikroszolgáltatáshoz. Ha a rendelési mikroszolgáltatás további mikroszolgáltatásokat hív meg HTTP használatával ugyanabban a kérési/válaszciklusban, HTTP-hívásláncot hoz létre. Kezdetben ésszerűnek tűnhet. A következő útvonalon azonban fontos szempontokat érdemes figyelembe venni:

  • Blokkolás és alacsony teljesítmény. A HTTP szinkron jellege miatt az eredeti kérés nem kap választ, amíg az összes belső HTTP-hívás be nem fejeződik. Tegyük fel, hogy ezeknek a hívásoknak a száma jelentősen megnő, és ugyanakkor a mikroszolgáltatásba irányuló köztes HTTP-hívások egyikét letiltja. Az eredmény az, hogy a teljesítmény befolyásolja a teljesítményt, és a további HTTP-kérések növekedésével exponenciálisan befolyásolja az általános méretezhetőséget.

  • Mikroszolgáltatások összekapcsolása HTTP-vel. Az üzleti mikroszolgáltatásokat nem szabad más üzleti mikroszolgáltatásokkal összekapcsolni. Ideális esetben nem szabad "tudniuk" más mikroszolgáltatások meglétéről. Ha az alkalmazás a példához hasonlóan a mikroszolgáltatások összekapcsolására támaszkodik, a mikroszolgáltatásonkénti autonómia elérése szinte lehetetlen lesz.

  • Hiba bármelyik mikroszolgáltatásban. Ha HTTP-hívások által összekapcsolt mikroszolgáltatások láncát implementálta, a mikroszolgáltatások bármelyikének sikertelensége (és végül sikertelen lesz) a mikroszolgáltatások teljes lánca meghiúsul. A mikroszolgáltatás-alapú rendszert úgy kell megtervezni, hogy a részleges hibák során a lehető legnagyobb mértékben működjön. Még ha az újrapróbálkozást exponenciális visszakapcsolási vagy megszakító mechanizmusokkal használó ügyféllogikát is implementál, annál összetettebbek a HTTP-hívásláncok, annál összetettebb a HTTP-n alapuló hibastratégia implementálása.

Valójában, ha a belső mikroszolgáltatások http-kérelmek láncainak létrehozásával kommunikálnak a leírt módon, azzal érvelhet, hogy monolitikus alkalmazással rendelkezik, de a folyamatok közötti HTTP-alapú, nem pedig folyamaton belüli kommunikációs mechanizmusokon.

Ezért a mikroszolgáltatás-autonómia érvényesítése és a nagyobb rugalmasság érdekében minimalizálnia kell a kérés-válasz kommunikáció láncainak használatát a mikroszolgáltatások között. Javasoljuk, hogy csak aszinkron interakciót használjon a mikroszolgáltatások közötti kommunikációhoz, akár aszinkron üzenet- és eseményalapú kommunikációval, akár (aszinkron) HTTP-lekérdezéssel az eredeti HTTP-kérés-/válaszciklustól függetlenül.

Az aszinkron kommunikáció használatát az útmutató későbbi részében további részletekkel ismertetjük, az Aszinkron mikroszolgáltatás-integráció pedig kikényszeríti a mikroszolgáltatás önállóságát és az aszinkron üzenetalapú kommunikációt.

További erőforrások