A mikroszolgáltatások közötti kommunikációnak hatékonynak és robusztusnak kell lennie. A sok kis szolgáltatás egyetlen üzleti tevékenység elvégzéséhez használ, ez kihívást jelenthet. Ebben a cikkben az aszinkron üzenetkezelé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ó kialakításának 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 ezen kihívások nagy részének 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últerheli a kéréseket, é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 a szolgáltatásközi hálózati hívások rugalmasságának kialakításában:
Újrapróbálkozás. Előfordulhat, hogy egy hálózati hívás meghiúsul egy önmagától eltávolodó átmeneti hiba miatt. A feladat teljes leállása helyett a hívónak általában újra kell próbálkoznia a művelettel egy bizonyos számú alkalommal, vagy amíg egy konfigurált időtúllépési időszak el nem telik. 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ó nem kap választ. Ha a hívó újrapróbálkozott, a művelet kétszer is meghívható. Általában nem biztonságos újrapróbálkozni a POST vagy a PATCH metódusokkal, 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 megkíséreljen 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 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 forgalom iptable szabályokkal továbbítódik egy podra. Alapértelmezés szerint véletlenszerű pod van kiválasztva. A szolgáltatáshálók (lásd alább) intelligensebb terheléselosztási algoritmusokat biztosítanak 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 akkor is, ha minden szolgáltatás naplókat és metrikákat hoz létre, anélkül, hogy valamilyen módon összekapcsolni őket, korlátozottan használhatók. A naplózás és a monitorozás című cikk az elosztott nyomkövetésről szól, de itt kihívásként említjük meg.
Szolgáltatás verziószámozása. Amikor egy csapat üzembe helyezi egy szolgáltatás új verzióját, el kell kerülnie a szolgáltatástó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ást .
TLS-titkosítás és kölcsönös TLS-hitelesítés. Biztonsági okokból érdemes lehet tls-lel titkosítani a szolgáltatások közötti forgalmat, és kölcsönös TLS-hitelesítéssel hitelesíteni a hívókat.
Szinkron és aszinkron üzenetkezelés
A mikroszolgáltatások két alapvető üzenetkezelési mintát használhatnak más mikroszolgáltatásokkal való kommunikációhoz.
Szinkron kommunikáció. Ebben a mintában egy szolgáltatás meghív egy API-t, amelyet egy másik szolgáltatás tesz elérhetővé egy olyan protokoll használatával, mint a HTTP vagy a gRPC. Ez a beállítás szinkron üzenetkezelési minta, mert a hívó a fogadó válaszára vár.
Aszinkron üzenet átadása. Ebben a mintában a szolgáltatás válasz nélkül küld üzenetet, é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 nem lesz blokkolva, amíg az I/O befejeződik. Ez fontos a teljesítmény szempontjából, de az architektúra szempontjából a megvalósítás részletei. Az aszinkron protokoll azt jelenti, hogy a feladó nem várja meg a választ. A HTTP egy szinkron protokoll, annak ellenére, hogy egy HTTP-ügyfél aszinkron I/O-t használhat a kérés küldésekor.
Minden mintának vannak kompromisszumoi. A kérés/válasz egy jól ismert paradigma, így az API tervezése természetesebbnek tűnhet, mint egy üzenetkezelő rendszer tervezé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ő. Pub/almodell használatával több felhasználó is feliratkozhat események fogadására. Lásd: Eseményvezérelt architektúrastílus.
Hibaelkülönítés. Ha a fogyasztó sikertelen, a feladó továbbra is küldhet üzeneteket. A rendszer az üzeneteket a fogyasztó helyreállításakor veszi át. Ez a képesség különösen hasznos a mikroszolgáltatás-architektúrában, mivel mindegyik szolgáltatás saját életciklussal rendelkezik. 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észség. A felsőbb rétegbeli szolgáltatások gyorsabban válaszolhatnak, ha nem várakoznak az alsóbb rétegbeli szolgáltatásokra. Ez különösen hasznos mikroszolgáltatás-architektúrákban. Ha szolgáltatási függőségek lánca van (az A szolgáltatás B hívása, amely C-t hív, és így tovább), a szinkron hívásokra való várakozás elfogadhatatlan mennyiségű késést adhat.
Terheléskiegyenlítés. Az üzenetsorok pufferként is működhetnek a számítási feladat simításához, így a fogadók saját ütemben dolgozhatják fel az üzeneteket.
Munkafolyamatok. Az üzenetsorok a munkafolyamatok kezelésére használhatók, ha a munkafolyamat minden lépése után ellenőrzik az üzenetet.
Az aszinkron üzenetkezelés hatékony használata azonban nehézségekbe ütközik.
Összekapcsolás az üzenetkezelési infrastruktúrával. 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égek. 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 duplikálással vagy művelet idempotenssé tételével kell kezelnie. A kérés-válasz szemantikát aszinkron üzenetkezeléssel is nehéz implementálni. A válasz küldéséhez egy másik üzenetsorra van szükség, valamint a kérések és 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 lekérdezési művelet szükséges. Emellett az üzenetsor szemantikája általában valamilyen zárolást igényel az üzenetkezelési infrastruktúrán belül. Ha az üzenetsor felügyelt szolgáltatás, további késések léphetnek fel, mivel az üzenetsor a fürt virtuális hálózatán kívül esik. Ezeket a problémákat az üzenetek kötegelésével háríthatja el, de ez bonyolítja a kódot. Ha az üzenetek nem igényelnek üzenetsor-szemantikát, előfordulhat, hogy üzenetsor helyett eseménystreamet is használhat. További információ: Eseményvezérelt architektúrastílus.
Drónos ké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:
Az Ingestion 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. A betöltéshez szükséges terhelésszintezés implementálásához aszinkron üzenetekre van szükség.
A fiók-, kézbesítési, csomag-, drón- és külső szállítási 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ára. 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 meghibásodása 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 alárendelt 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-átjáró című cikkben azt ismertetjük, hogyan rejtheti el az API-átjáró az alapul szolgáló szolgáltatásokat az ügyfél elől, így az ügyfélnek nem kell tudnia, hogy mely szolgáltatások mely API-kat teszik elérhetővé.
Amíg egy drón repül, 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 egy 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
vagyDeliveryCompleted
. 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 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 munkafolyamat fő útvonalát.
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 ledob 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. A korábban ismertetett módon a Drónkezelés egy külön, határolt környezetbe tartozik. A drónesemények egy drón fizikai helyét közvetítik. A kézbesítési események viszont egy másik üzleti entitás, a kézbesítés állapotának változásait jelölik.
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 lettek kialakítva, hogy kezelni tudják az előző szakaszban felsorolt problémákat, és hogy ezekért az aggodalmakért felelősséget helyezzenek el maguktól a mikroszolgáltatásoktól, és egy megosztott rétegbe. 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.
Feljegyzés
A Service Mesh a Nagykövet minta példája – egy segítő szolgáltatás, 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ések. Ezt a maximális újrapróbálkozási számot és egy időtúllépési időszakot konfigurálhatja a maximális késés kötéséhez.
Megszakítja a kapcsolatcsoportot. Ha egy példány következetesen sikertelen kéréseket ad meg, 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 rögzíti a szolgáltatásközi hívásokkal kapcsolatos metrikákat, például a kérések mennyiségét, a késést, a hiba- és sikerességi arányokat, valamint a válaszméreteket. 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? Ez a konkrét licenctő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 service mesh azonban összetettebbé teszi a fürt beállítását és konfigurálását. Lehetséges, hogy teljesítménybeli következményekkel jár, mert 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 gyakori kihívása a több szolgáltatásra kiterjedő tranzakciók helyes kezelése. Ebben a forgatókönyvben a tranzakció sikeressége gyakran teljes vagy semmi – ha az egyik résztvevő szolgáltatás meghiúsul, a teljes tranzakciónak sikertelennek kell lennie.
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 újrapróbálkozásával. Ha a művelet bizonyos számú kísérlet után is meghiúsul, akkor nem átmeneti hibának számít.
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. Kezeletlen 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 a már sikeres tranzakció egyéb lépéseit is vissza kell vonni.
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 Drone 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 a műveletet külső rendszernek vagy akár manuális folyamatnak kell elvégeznie. Ne feledje, hogy a kompenzáló mértékek is hibásak.
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 egy lemondási API-t hív meg a kompenzálni kívánt szolgáltatásokon. Ez a Scheduler-ügynök felügyelői mintájának változata. A Felügyelő 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ópultra.
Maga az Ütemező 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 tartós tárolóba mentése 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 a munkát, ahol az előző példány abbahagyta. Az ellenőrzőpontok írása azonban teljesítménybeli többletterhelést okozhat.
Egy másik lehetőség az, hogy az összes műveletet idempotensnek kell megtervezni. 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.