Szerkesztés

Share via


Szolgáltatásközi kommunikáció tervezése mikroszolgáltatásokhoz

Azure DevOps

A mikroszolgáltatások közötti kommunikációnak hatékonynak és robusztusnak kell lennie. Ez kihívást jelenthet, ha sok kis szolgáltatás kommunikál egyetlen üzleti tevékenység elvégzésére. Ebben a cikkben az aszinkron üzenetküldés és a szinkron API-k közötti kompromisszumokat tekintjük át. Ezután áttekintjük a rugalmas szolgáltatásközi kommunikáció tervezésének néhány kihívását.

Problémák

Íme néhány, a szolgáltatásközi kommunikációból eredő fő kihívás. A jelen cikk későbbi részében ismertetett szolgáltatáshálók ezeknek a kihívásoknak a kezelésére szolgálnak.

Rugalmasság. Egy adott mikroszolgáltatás több tucat vagy akár több száz példánya is lehet. A példányok számos okból meghiúsulhatnak. Előfordulhat csomópontszintű hiba, például hardverhiba vagy virtuális gép újraindítása. Előfordulhat, hogy egy példány összeomlik, vagy túlterhelt a kérésekkel, és nem tudja feldolgozni az új kéréseket. Ezen események bármelyike a hálózati hívás meghiúsulását okozhatja. Két tervezési minta segíthet rugalmasabbá tenni a szolgáltatásközi hálózati hívásokat:

  • Próbálkozzon újra. Előfordulhat, hogy egy hálózati hívás egy önmagában megszűnő átmeneti hiba miatt hiúsul meg. Ahelyett, hogy a feladat egyenesen meghiúsul, a hívónak általában újra kell próbálkoznia a művelettel egy bizonyos számú alkalommal, vagy amíg el nem telik egy konfigurált időtúllépési időszak. Ha azonban egy művelet nem idempotens, az újrapróbálkozások nem kívánt mellékhatásokat okozhatnak. Az eredeti hívás sikeres lehet, de a hívó soha nem kap választ. Ha a hívó újrapróbálkozott, a művelet kétszer is meghívható. Általában nem biztonságos a POST vagy PATCH metódusok újrapróbálkozása, mert ezek nem garantáltan idempotensek.

  • Megszakító. A túl sok sikertelen kérés szűk keresztmetszetet okozhat, mivel a függőben lévő kérések halmozódnak fel az üzenetsorban. Ezek a blokkolt kérelmek olyan kritikus rendszererőforrásokat akadályozhatnak, mint a memória, szálak, adatbázis-kapcsolatok stb., ezáltal pedig kaszkádolt meghibásodásokhoz vezethetnek. Az áramkör-megszakító minta megakadályozhatja, hogy egy szolgáltatás ismétlődően megpróbáljon egy valószínűleg sikertelen műveletet.

Terheléselosztás. Amikor az "A" szolgáltatás meghívja a "B" szolgáltatást, a kérésnek el kell érnie a "B" szolgáltatás egy futó példányát. A Kubernetesben az Service erőforrástípus stabil IP-címet biztosít a podok egy csoportjához. A szolgáltatás IP-címére vonatkozó hálózati forgalmat iptable szabályokkal továbbítják egy podra. Alapértelmezés szerint egy véletlenszerű pod van kiválasztva. A szolgáltatáshálók (lásd alább) intelligensebb terheléselosztási algoritmusokat biztosíthatnak a megfigyelt késés vagy más metrikák alapján.

Elosztott nyomkövetés. Egyetlen tranzakció több szolgáltatásra is kiterjedhet. Ez megnehezítheti a rendszer általános teljesítményének és állapotának monitorozását. Még ha minden szolgáltatás naplókat és metrikákat hoz létre, anélkül, hogy valamilyen módon összekapcsolnák őket, korlátozottan használhatók. A Naplózás és figyelés című cikk többet foglalkozik az elosztott nyomkövetéssel, de ezt itt kihívásként említjük meg.

Szolgáltatás verziószámozása. Amikor egy csapat üzembe helyez egy szolgáltatás új verzióját, el kell kerülnie az attól függő egyéb szolgáltatások vagy külső ügyfelek feltörését. Emellett érdemes lehet egy szolgáltatás több verzióját egymás mellett futtatni, és a kéréseket egy adott verzióra irányítani. A probléma további megvitatásához tekintse meg az API-verziószámozás című témakört.

TLS-titkosítás és kölcsönös TLS-hitelesítés. Biztonsági okokból érdemes lehet a szolgáltatások közötti forgalmat TLS-lel titkosítani, és kölcsönös TLS-hitelesítéssel hitelesíteni a hívókat.

Szinkron és aszinkron üzenetküldés

A mikroszolgáltatások két alapvető üzenetkezelési mintát használhatnak más mikroszolgáltatásokkal való kommunikációhoz.

  1. Szinkron kommunikáció. Ebben a mintában egy szolgáltatás meghív egy API-t, amelyet egy másik szolgáltatás tesz közzé egy olyan protokoll használatával, mint a HTTP vagy a gRPC. Ez a beállítás szinkron üzenetküldési minta, mert a hívó a fogadó válaszára vár.

  2. Aszinkron üzenet továbbítása. Ebben a mintában a szolgáltatás nem vár választ, és egy vagy több szolgáltatás aszinkron módon dolgozza fel az üzenetet.

Fontos különbséget tenni az aszinkron I/O és az aszinkron protokoll között. Az aszinkron I/O azt jelenti, hogy a hívó szál nincs blokkolva, amíg az I/O befejeződik. Ez a teljesítmény szempontjából fontos, de az architektúra szempontjából egy megvalósítási részlet. Az aszinkron protokoll azt jelenti, hogy a feladó nem vár választ. A HTTP szinkron protokoll, annak ellenére, hogy a HTTP-ügyfél aszinkron I/O-t használhat a kérés küldésekor.

Minden mintának vannak kompromisszumai. A kérés/válasz egy jól ismert paradigma, így az API tervezése természetesebbnek tűnhet, mint egy üzenetkezelési rendszer megtervezése. Az aszinkron üzenetkezelésnek azonban vannak olyan előnyei, amelyek hasznosak lehetnek a mikroszolgáltatás-architektúrában:

  • Csökkentett csatlakozó. Az üzenet feladójának nem kell tudnia a fogyasztóról.

  • Több előfizető. Egy pub/almodell használatával több fogyasztó is feliratkozhat az események fogadására. Lásd: Eseményvezérelt architektúrastílus.

  • Hibaelkülönítés. Ha a fogyasztó meghibásodik, a feladó továbbra is küldhet üzeneteket. Az üzeneteket a rendszer akkor veszi fel, amikor a fogyasztó helyreáll. Ez a képesség különösen hasznos egy mikroszolgáltatás-architektúrában, mivel minden szolgáltatásnak saját életciklusa van. Egy szolgáltatás bármikor elérhetetlenné válhat, vagy lecserélhető egy újabb verzióra. Az aszinkron üzenetkezelés képes kezelni az időszakos állásidőt. A szinkron API-k viszont megkövetelik, hogy az alsóbb rétegbeli szolgáltatás elérhető legyen, vagy a művelet meghiúsul.

  • Válaszképesség. A felsőbb rétegbeli szolgáltatások gyorsabban válaszolhatnak, ha nem várakoznak az alárendelt szolgáltatásokra. Ez különösen hasznos mikroszolgáltatás-architektúrákban. Ha szolgáltatásfüggőségek lánca van (az A szolgáltatás meghívja a B szolgáltatást, amely meghívja a C-t stb.), a szinkron hívásokra való várakozás elfogadhatatlan mennyiségű késést adhat.

  • Terhelésszintezés. Az üzenetsorok pufferként működhetnek a számítási feladatok simításához, így a fogadók saját ütemben dolgozhatják fel az üzeneteket.

  • Munkafolyamatok. Az üzenetsorok munkafolyamatok kezelésére használhatók, ha a munkafolyamat minden lépése után ellenőrzik az üzenetet.

Ugyanakkor az aszinkron üzenetkezelés hatékony használata is kihívást jelent.

  • Csatlakozás az üzenetkezelési infrastruktúrához. Egy adott üzenetkezelési infrastruktúra használata szoros összekapcsolást okozhat az infrastruktúrával. Később nehéz lesz másik üzenetkezelési infrastruktúrára váltani.

  • Késés. Egy művelet végpontok közötti késése magas lehet, ha az üzenetsorok megtelnek.

  • Költség. Magas átviteli sebesség esetén az üzenetkezelési infrastruktúra pénzügyi költsége jelentős lehet.

  • Összetettség. Az aszinkron üzenetkezelés nem triviális feladat. A duplikált üzeneteket például a duplikálás megszüntetésével vagy a műveletek idempotenssé tételével kell kezelnie. A kérés-válasz szemantikát az aszinkron üzenetkezeléssel is nehéz implementálni. A válasz elküldéséhez egy másik üzenetsorra van szüksége, valamint a kérések és a válaszüzenetek korrelációjának módja.

  • Átviteli sebesség. Ha az üzenetek üzenetsor-szemantikát igényelnek, az üzenetsor szűk keresztmetszetté válhat a rendszerben. Minden üzenethez legalább egy üzenetsor-művelet és egy dequeue művelet szükséges. Ezenkívül az üzenetsor szemantika általában valamilyen zárolást igényel az üzenetkezelési infrastruktúrán belül. Ha az üzenetsor felügyelt szolgáltatás, előfordulhat, hogy további késés tapasztalható, mivel az üzenetsor külső a fürt virtuális hálózatán. Ezeket a problémákat az üzenetek kötegelésével háríthatja el, de ez bonyolítja a kódot. Ha az üzenetekhez nincs szükség üzenetsor-szemantika használatára, előfordulhat, hogy az üzenetsor helyett egy eseménystreamet is használhat. További információ: Eseményvezérelt architektúrastílus.

Drónkézbesítés: Az üzenetkezelési minták kiválasztása

Ez a megoldás a Drone Delivery példáját használja. Ideális a repülőgép- és repülőgépipar számára.

Ezeket a szempontokat szem előtt tartva a fejlesztői csapat a következő tervezési döntéseket hozta a Drone Delivery alkalmazáshoz:

  • A Betöltési szolgáltatás nyilvános REST API-t tesz elérhetővé, amelyet az ügyfélalkalmazások a szállítások ütemezéséhez, frissítéséhez vagy lemondásához használnak.

  • A betöltési szolgáltatás az Event Hubs használatával küld aszinkron üzeneteket a Scheduler szolgáltatásnak. Az aszinkron üzenetekre a betöltéshez szükséges terhelési simítás implementálásához van szükség.

  • A Fiók, a Kézbesítés, a Csomag, a Drón és a Harmadik féltől származó átviteli szolgáltatások mindegyike belső REST API-kat tesz elérhetővé. A Scheduler szolgáltatás meghívja ezeket az API-kat egy felhasználói kérés végrehajtásához. A szinkron API-k használatának egyik oka, hogy az ütemezőnek választ kell kapnia az egyes alárendelt szolgáltatásoktól. Az alsóbb rétegbeli szolgáltatások bármelyikének hibája azt jelenti, hogy a teljes művelet meghiúsult. Lehetséges probléma azonban a háttérszolgáltatások meghívásával bevezetett késés mennyisége.

  • Ha bármely alsóbb rétegbeli szolgáltatás nem átmeneti hibával rendelkezik, a teljes tranzakciót sikertelenként kell megjelölni. Az eset kezeléséhez az Ütemező szolgáltatás aszinkron üzenetet küld a felügyelőnek, hogy a felügyelő ütemezhesse a kompenzáló tranzakciókat.

  • A kézbesítési szolgáltatás nyilvános API-t tesz elérhetővé, amellyel az ügyfelek lekérhetik a kézbesítés állapotát. Az API Gateway című cikkben azt ismertetjük, hogyan rejtheti el az API-átjáró a mögöttes szolgáltatásokat az ügyfél elől, így az ügyfélnek nem kell tudnia, hogy mely szolgáltatások teszik elérhetővé az api-kat.

  • Amíg egy drón repülés közben van, a Drón szolgáltatás olyan eseményeket küld, amelyek tartalmazzák a drón aktuális helyét és állapotát. A kézbesítési szolgáltatás figyeli ezeket az eseményeket a kézbesítés állapotának nyomon követése érdekében.

  • Amikor a kézbesítés állapota megváltozik, a kézbesítési szolgáltatás kézbesítési állapot eseményt küld, például DeliveryCreated vagy DeliveryCompleted. Bármely szolgáltatás feliratkozhat ezekre az eseményekre. A jelenlegi kialakításban a Kézbesítési előzmények szolgáltatás az egyetlen előfizető, de később más előfizetők is lehetnek. Előfordulhat például, hogy az események egy valós idejű elemzési szolgáltatásba kerülnek. Mivel az ütemezőnek nem kell várnia a válaszra, a további előfizetők hozzáadása nem befolyásolja a fő munkafolyamat-útvonalat.

Drónkommunikáció diagramja

Figyelje meg, hogy a kézbesítési állapot eseményei a drón helyének eseményeiből származnak. Ha például egy drón elér egy kézbesítési helyet, és eldob egy csomagot, a kézbesítési szolgáltatás ezt egy DeliveryCompleted eseményre fordítja. Ez egy példa a tartománymodellek gondolkodására. Ahogy korábban említettem, a Drónkezelés egy külön, korlátozott környezetben található. A drónesemények egy drón fizikai helyét közvetítik. A kézbesítési események viszont a kézbesítés állapotának változásait jelentik, amely egy másik üzleti entitás.

Szolgáltatásháló használata

A szolgáltatásháló egy olyan szoftverréteg, amely a szolgáltatásközi kommunikációt kezeli. A szolgáltatáshálók úgy vannak kialakítva, hogy foglalkozzanak az előző szakaszban felsorolt számos aggálysal, és hogy az ilyen problémákért felelősséget helyezzenek el maguktól a mikroszolgáltatásoktól és egy megosztott réteghez. A szolgáltatásháló proxyként működik, amely lehallgatja a fürt mikroszolgáltatásai közötti hálózati kommunikációt. A service mesh koncepció jelenleg elsősorban a tárolóvezénylőkre vonatkozik, nem pedig a kiszolgáló nélküli architektúrákra.

Megjegyzés

A Service Mesh egy példa az Ambassador mintára – egy segítő szolgáltatásra, amely hálózati kéréseket küld az alkalmazás nevében.

Jelenleg a Kubernetes szolgáltatáshálójának fő lehetőségei a Linkerd és az Istio. Mindkét technológia gyorsan fejlődik. A Linkerd és az Istio közös funkciói azonban a következők:

  • Terheléselosztás a munkamenet szintjén a megfigyelt késések vagy a függőben lévő kérelmek száma alapján. Ez javíthatja a Kubernetes által biztosított 4. rétegbeli terheléselosztás teljesítményét.

  • 7. rétegbeli útválasztás URL-útvonal, gazdagépfejléc, API-verzió vagy más alkalmazásszintű szabályok alapján.

  • Sikertelen kérések újrapróbálkozása. A szolgáltatáshálók megértik a HTTP-hibakódokat, és automatikusan újrapróbálkozhatnak a sikertelen kérésekkel. Az újrapróbálkozási próbálkozások maximális számát és egy időtúllépési időtartamot is konfigurálhatja a maximális késés kötéséhez.

  • Megszakítja a kapcsolatcsoportot. Ha egy példány folyamatosan meghiúsul, a szolgáltatásháló ideiglenesen elérhetetlenként jelöli meg. Egy visszalépési időszak után újra megpróbálja a példányt. Az áramkör-megszakítót különböző feltételek, például az egymást követő hibák száma alapján konfigurálhatja,

  • A Service Mesh metrikákat rögzít a szolgáltatásközi hívásokról, például a kérelmek mennyiségéről, a késésről, a hibák és a sikerek arányáról, valamint a válaszméretekről. A szolgáltatásháló az elosztott nyomkövetést is lehetővé teszi, ha korrelációs információkat ad hozzá a kérések egyes ugrásaihoz.

  • Kölcsönös TLS-hitelesítés szolgáltatásközi hívásokhoz.

Szüksége van szolgáltatáshálóra? Attól függ. Szolgáltatásháló nélkül figyelembe kell vennie a cikk elején említett kihívásokat. Az olyan problémákat, mint az újrapróbálkozás, az áramkör-megszakító és az elosztott nyomkövetés szolgáltatásháló nélkül is megoldható, a szolgáltatásháló azonban ezeket a problémákat az egyes szolgáltatásokból egy dedikált rétegbe helyezi át. A szolgáltatásháló azonban összetettebbé teszi a fürt beállítását és konfigurálását. Előfordulhatnak teljesítménybeli következmények, mivel a kérések mostantól a szolgáltatásháló-proxyn keresztül lesznek irányítva, és mivel a fürt minden csomópontján további szolgáltatások futnak. A szolgáltatásháló éles környezetben való üzembe helyezése előtt alapos teljesítmény- és terheléstesztelést kell végeznie.

Elosztott tranzakciók

A mikroszolgáltatások egyik gyakori kihívása a több szolgáltatásra kiterjedő tranzakciók helyes kezelése. Ebben a forgatókönyvben gyakran egy tranzakció sikeressége vagy semmi – ha az egyik résztvevő szolgáltatás meghibásodik, a teljes tranzakciónak meghiúsulnia kell.

Két esetet kell figyelembe venni:

  • Egy szolgáltatás átmeneti hibát, például hálózati időtúllépést tapasztalhat. Ezek a hibák gyakran egyszerűen megoldhatók a hívás ismételt megkísérlésével. Ha a művelet bizonyos számú kísérlet után is meghiúsul, az nem átmeneti hibának minősül.

  • A nem átmeneti hiba minden olyan hiba, amely nem valószínű, hogy magától elmúlik. A nem átmeneti hibák közé tartoznak a normál hibafeltételek, például az érvénytelen bemenetek. Nem kezelt kivételeket is tartalmaznak az alkalmazáskódban, vagy egy folyamat összeomlik. Ilyen típusú hiba esetén a teljes üzleti tranzakciót hibaként kell megjelölni. Előfordulhat, hogy vissza kell vonnia a már sikeres tranzakció egyéb lépéseit.

Nem átmeneti hiba után előfordulhat, hogy az aktuális tranzakció részben meghiúsult állapotban van, ahol egy vagy több lépés már sikeresen befejeződött. Ha például a Drón szolgáltatás már ütemezett egy drónt, a drónt le kell mondani. Ebben az esetben az alkalmazásnak vissza kell vonnia a sikeres lépéseket egy kompenzáló tranzakció használatával. Bizonyos esetekben ezt külső rendszernek vagy akár manuális folyamatnak kell elvégeznie.

Ha a tranzakciók kompenzálásának logikája összetett, érdemes lehet létrehozni egy külön szolgáltatást, amely felelős a folyamatért. A Drone Delivery alkalmazásban a Scheduler szolgáltatás egy dedikált üzenetsorba helyezi a sikertelen műveleteket. Ebből az üzenetsorból egy külön, Felügyelő nevű mikroszolgáltatás olvas, és meghív egy lemondási API-t a kompenzálni kívánt szolgáltatásokhoz. Ez a Scheduler-ügynök felügyeleti mintájának egy változata. A Felügyeleti szolgáltatás más műveleteket is végrehajthat, például sms-ben vagy e-mailben értesítheti a felhasználót, vagy riasztást küldhet egy műveleti irányítópultnak.

A Felügyelő mikroszolgáltatást bemutató ábra

Maga a Scheduler szolgáltatás meghiúsulhat (például egy csomópont összeomlik). Ebben az esetben egy új példány felpöröghet, és átveheti azokat. A már folyamatban lévő tranzakciókat azonban folytatni kell.

Az egyik módszer egy ellenőrzőpont mentése egy tartós tárolóba a munkafolyamat minden lépésének befejezése után. Ha az Ütemező szolgáltatás egy példánya összeomlik egy tranzakció közepén, egy új példány az ellenőrzőpont használatával folytathatja az előző példány leállását. Az ellenőrzőpontok írása azonban többletterhelést okozhat.

Egy másik lehetőség, hogy minden műveletet idempotensre tervez. A művelet idempotens, ha többször is meghívható anélkül, hogy az első hívás után további mellékhatásokat okoz. Az alsóbb rétegbeli szolgáltatásnak alapvetően figyelmen kívül kell hagynia az ismétlődő hívásokat, ami azt jelenti, hogy a szolgáltatásnak képesnek kell lennie az ismétlődő hívások észlelésére. Nem mindig egyszerű idempotens metódusokat implementálni. További információ: Idempotens műveletek.

Következő lépések

A közvetlenül egymással kommunikáló mikroszolgáltatások esetében fontos, hogy jól megtervezett API-kat hozzon létre.