Návrh komunikace mezi službami pro mikroslužby

Azure DevOps

Komunikace mezi mikroslužbami musí být efektivní a robustní. S mnoha malými službami, které interagují, aby dokončily jednu obchodní aktivitu, to může být výzva. V tomto článku se podíváme na kompromisy mezi asynchronním zasíláním zpráv a synchronními rozhraními API. Pak se podíváme na některé výzvy při návrhu odolné komunikace mezi službami.

Výzvy

Tady jsou některé z hlavních problémů vyplývajících z komunikace mezi službami. Sítě služeb popsané dále v tomto článku jsou navržené tak, aby zvládly mnoho z těchto problémů.

Odolnost. Můžou existovat desítky nebo dokonce stovky instancí jakékoli mikroslužby. Instance může selhat z mnoha důvodů. Může dojít k selhání na úrovni uzlu, jako je selhání hardwaru nebo restartování virtuálního počítače. Instance může dojít k chybovému ukončení nebo může být zahlcená požadavky a nemůže zpracovat žádné nové požadavky. Každá z těchto událostí může způsobit selhání síťového volání. Existují dva vzory návrhu, které můžou pomoct zajistit odolnost síťových volání mezi službami:

  • Zkuste to znovu. Síťové volání může selhat kvůli přechodné chybě, která zmizí sama. Místo toho, aby volající selhal přímo, měl by obvykle opakovat operaci určitou dobu nebo dokud neuplyne nakonfigurovaný časový limit. Pokud ale operace není idempotentní, může opakování způsobit nezamýšlené vedlejší účinky. Původní volání může být úspěšné, ale volající nikdy nedostane odpověď. Pokud volající provede opakování, může být operace vyvolána dvakrát. Obecně platí, že opakování metod POST nebo PATCH není bezpečné, protože tyto metody nejsou zaručené idempotentní.

  • Jistič. Příliš mnoho neúspěšných požadavků může způsobit kritický bod, protože čekající požadavky se hromadí ve frontě. Tyto blokované požadavky můžou blokovat důležité systémové prostředky jako paměť vlákna, připojení k databázím atd., což může způsobit další chyby. Model Jistič může zabránit službě v opakovaném pokusu o operaci, která pravděpodobně selže.

Vyrovnávání zatížení. Když služba "A" volá službu "B", požadavek musí kontaktovat spuštěnou instanci služby "B". V Kubernetes Service poskytuje typ prostředku stabilní IP adresu pro skupinu podů. Síťový provoz na IP adresu služby se přesměruje do podu pomocí pravidel iptable. Ve výchozím nastavení se vybere náhodný pod. Síť služeb (viz níže) může poskytovat inteligentnější algoritmy vyrovnávání zatížení na základě pozorované latence nebo jiných metrik.

Distribuované trasování. Jedna transakce může zahrnovat více služeb. To může ztížit monitorování celkového výkonu a stavu systému. I když každá služba generuje protokoly a metriky, aniž by je nějak spojila, mají omezené využití. Článek Protokolování a monitorování popisuje více o distribuovaném trasování, ale tady ho uvádíme jako výzvu.

Správa verzí služby. Když tým nasadí novou verzi služby, musí se vyhnout narušení jiných služeb nebo externích klientů, kteří na ní závisejí. Kromě toho můžete chtít spustit několik verzí služby vedle sebe a směrovat požadavky na konkrétní verzi. Další informace o tomto problému najdete v tématu Správa verzí rozhraní API .

Šifrování TLS a vzájemné ověřování TLS. Z bezpečnostních důvodů můžete chtít šifrovat provoz mezi službami pomocí protokolu TLS a k ověřování volajících použít vzájemné ověřování TLS.

Synchronní a asynchronní zasílání zpráv

Existují dva základní vzory zasílání zpráv, které můžou mikroslužby používat ke komunikaci s jinými mikroslužbami.

  1. Synchronní komunikace. V tomto modelu služba volá rozhraní API, které zpřístupňuje jiná služba, pomocí protokolu, jako je HTTP nebo gRPC. Tato možnost je synchronní způsob zasílání zpráv, protože volající čeká na odpověď od příjemce.

  2. Asynchronní předávání zpráv. V tomto modelu služba odesílá zprávu bez čekání na odpověď a jedna nebo více služeb zprávu zpracuje asynchronně.

Je důležité rozlišovat mezi asynchronními vstupně-výstupními operacemi a asynchronním protokolem. Asynchronní vstupně-výstupní operace znamená, že volající vlákno není během dokončení vstupně-výstupních operací blokováno. To je důležité z hlediska výkonu, ale jedná se o podrobnosti implementace z hlediska architektury. Asynchronní protokol znamená, že odesílatel nečeká na odpověď. HTTP je synchronní protokol, i když klient HTTP může při odesílání požadavku používat asynchronní vstupně-výstupní operace.

Každý model má kompromisy. Požadavek/odpověď je dobře známé paradigma, takže návrh rozhraní API může být přirozenější než návrh systému zasílání zpráv. Asynchronní zasílání zpráv má ale některé výhody, které mohou být užitečné v architektuře mikroslužeb:

  • Redukovaná spojka. Odesílatel zprávy nemusí o příjemci vědět.

  • Více odběratelů. Pomocí modelu pub/sub se může k odběru událostí přihlásit více uživatelů. Viz Styl architektury řízené událostmi.

  • Izolace selhání. Pokud příjemce selže, odesílatel může zprávy posílat i nadále. Zprávy budou vyzvednuty, jakmile se příjemce obnoví. Tato schopnost je zvlášť užitečná v architektuře mikroslužeb, protože každá služba má svůj vlastní životní cyklus. Služba může být kdykoli nedostupná nebo může být nahrazena novější verzí. Asynchronní zasílání zpráv může zpracovávat přerušované výpadky. Synchronní rozhraní API na druhou stranu vyžadují, aby byla k dispozici podřízená služba nebo operace selže.

  • Rychlost odezvy. Upstreamová služba může reagovat rychleji, pokud nečeká na podřízené služby. To je užitečné zejména v architektuře mikroslužeb. Pokud existuje řetěz závislostí služby (služba A volá B, která volá C atd.), čekání na synchronní volání může přidat nepřijatelnou latenci.

  • Vyrovnávání zatížení. Fronta může fungovat jako vyrovnávací paměť pro vyrovnávání zatížení, aby příjemci mohli zpracovávat zprávy vlastní rychlostí.

  • Pracovní postupy. Fronty se dají použít ke správě pracovního postupu tak, že zprávu za každým krokem v pracovním postupu nasměrují kontrolou.

Existují však také určité problémy s efektivním používáním asynchronního zasílání zpráv.

  • Propojení s infrastrukturou zasílání zpráv Použití konkrétní infrastruktury zasílání zpráv může způsobit těsné propojení s danou infrastrukturou. Později bude obtížné přepnout na jinou infrastrukturu zasílání zpráv.

  • Latence. Pokud se fronty zpráv zaplní, může být latence celé operace vysoká.

  • Náklady. Při vysoké propustnosti můžou být peněžní náklady na infrastrukturu zasílání zpráv značné.

  • Složitost: Zpracování asynchronního zasílání zpráv není triviální úkol. Například musíte zpracovávat duplicitní zprávy, a to buď odstraněním duplicit, nebo vytvořením idempotentních operací. Je také obtížné implementovat sémantiku požadavků a odpovědí pomocí asynchronního zasílání zpráv. K odeslání odpovědi potřebujete další frontu a také způsob korelace požadavků a odpovědí.

  • Propustnost: Pokud zprávy vyžadují sémantiku front, může se fronta stát kritickým bodem v systému. Každá zpráva vyžaduje alespoň jednu operaci fronty a jednu operaci vyřazení z fronty. Kromě toho sémantika front obecně vyžaduje určitý druh uzamčení uvnitř infrastruktury zasílání zpráv. Pokud je fronta spravovanou službou, může dojít k další latenci, protože fronta je externí pro virtuální síť clusteru. Tyto problémy můžete zmírnit dávkováním zpráv, ale to kód komplikuje. Pokud zprávy nevyžadují sémantiku front, můžete místo fronty použít stream událostí. Další informace najdete v tématu Styl architektury řízený událostmi.

Doručování pomocí dronů: Volba vzorů zasílání zpráv

Toto řešení používá příklad doručování pomocí dronů. Je ideální pro letecký a letecký průmysl.

Vývojový tým s ohledem na tyto aspekty provedl následující volby návrhu aplikace Pro doručování pomocí dronů:

  • Služba příjmu dat zveřejňuje veřejné rozhraní REST API, které klientské aplikace používají k plánování, aktualizaci nebo rušení dodávek.

  • Služba příjmu dat používá službu Event Hubs k odesílání asynchronních zpráv do služby Scheduler. Asynchronní zprávy jsou nezbytné k implementaci vyrovnávání zatížení, které je vyžadováno pro příjem dat.

  • Všechny služby Account, Delivery, Package, Drone a Third-party Transport zpřístupňují interní rozhraní REST API. Služba Scheduler volá tato rozhraní API k provedení požadavku uživatele. Jedním z důvodů, proč používat synchronní rozhraní API, je to, že plánovač potřebuje získat odpověď z každé podřízené služby. Selhání v některé z podřízených služeb znamená, že celá operace selhala. Potenciálním problémem je však míra latence, která se zavádí voláním back-endových služeb.

  • Pokud má některá podřízená služba nepřesnou chybu, celá transakce by měla být označena jako neúspěšná. Aby bylo možné tento případ vyřešit, služba Scheduler odešle asynchronní zprávu správce, aby správce mohl naplánovat kompenzační transakce.

  • Služba Delivery zpřístupňuje veřejné rozhraní API, které můžou klienti použít k získání stavu doručení. V článku Brána rozhraní API popisujeme, jak brána rozhraní API může před klientem skrýt základní služby, aby klient nemusel vědět, které služby zpřístupňují která rozhraní API.

  • Během letu dronu odesílá služba dronů události, které obsahují aktuální polohu a stav dronu. Služba doručování těmto událostem naslouchá, aby mohla sledovat stav doručení.

  • Když se stav doručení změní, služba doručení odešle událost stavu doručení, například DeliveryCreated nebo DeliveryCompleted. K odběru těchto událostí se může přihlásit jakákoli služba. V aktuálním návrhu je jediným odběratelem služba Historie doručení, ale později se můžou vyskytovat i další odběratelé. Události můžou například přejít do analytické služby v reálném čase. A protože plánovač nemusí čekat na odpověď, přidání dalších odběratelů nemá vliv na hlavní cestu pracovního postupu.

Diagram komunikace s drony

Všimněte si, že události stavu doručení jsou odvozené od událostí umístění dronů. Když například dron dosáhne místa doručení a vysadí balíček, služba doručení to převede na událost DeliveryCompleted. Toto je příklad myšlení z hlediska doménových modelů. Jak je popsáno výše, správa dronů patří do samostatného ohraničeného kontextu. Události dronů vyjadřují fyzickou polohu dronu. Události doručení naopak představují změny ve stavu doručení, což je jiná obchodní entita.

Použití sítě služeb

Síť služeb je softwarová vrstva, která zpracovává komunikaci mezi službami. Sítě služeb jsou navržené tak, aby řešily řadu problémů uvedených v předchozí části a přesunuly odpovědnost za tyto záležitosti ze samotných mikroslužeb do sdílené vrstvy. Síť služeb funguje jako proxy server, který zachycuje síťovou komunikaci mezi mikroslužbami v clusteru. V současné době se koncept service mesh vztahuje hlavně na orchestrátory kontejnerů, nikoli na bezserverové architektury.

Poznámka

Síť služeb je příkladem vzoru Ambassador – pomocné služby, která odesílá síťové požadavky jménem aplikace.

V současnosti jsou hlavními možnostmi sítě služeb v Kubernetes Linkerd a Istio. Obě tyto technologie se rychle vyvíjejí. Mezi funkce, které mají linkerd i Istio společné, patří:

  • Vyrovnávání zatížení na úrovni relace na základě pozorovaných latencí nebo počtu nevyřízených požadavků To může zlepšit výkon při vyrovnávání zatížení vrstvy 4, které poskytuje Kubernetes.

  • Směrování vrstvy 7 na základě cesty url, hlavičky hostitele, verze rozhraní API nebo jiných pravidel na úrovni aplikace.

  • Zkuste opakovat neúspěšné požadavky. Síť služeb rozumí kódům chyb HTTP a může automaticky opakovat neúspěšné požadavky. Můžete nakonfigurovat maximální počet opakování spolu s časovým limitem, aby se vázala maximální latence.

  • Přerušení okruhu. Pokud instance konzistentně selhává požadavky, síť služby ji dočasně označí jako nedostupnou. Po uplynutí doby zásady zkusí instanci znovu. Jistič můžete nakonfigurovat na základě různých kritérií, jako je počet po sobě jdoucích selhání.

  • Síť služeb zaznamenává metriky o voláních mezi službami, jako je objem požadavků, latence, míra chyb a úspěšnosti a velikosti odpovědí. Síť služby také umožňuje distribuované trasování přidáním informací o korelaci pro každý segment směrování v požadavku.

  • Vzájemné ověřování TLS pro volání mezi službami

Potřebujete síť služeb? To závisí na okolnostech. Bez sítě služeb budete muset zvážit všechny výzvy uvedené na začátku tohoto článku. Problémy, jako je opakování, jistič a distribuované trasování, můžete vyřešit bez sítě služeb, ale síť služeb přesune tyto problémy z jednotlivých služeb do vyhrazené vrstvy. Na druhou stranu síť služeb zvyšuje složitost nastavení a konfigurace clusteru. Může to mít vliv na výkon, protože požadavky se teď směrují přes proxy server sítě služeb a že na každém uzlu v clusteru jsou teď spuštěné další služby. Před nasazením sítě služeb v produkčním prostředí byste měli provést důkladné testování výkonu a zatížení.

Distribuované transakce

Běžnou výzvou v mikroslužbách je správné zpracování transakcí, které pokrývají více služeb. V tomto scénáři je často úspěch transakce vše nebo nic – pokud selže jedna ze zúčastněných služeb, musí selhat celá transakce.

Je potřeba zvážit dva případy:

  • U služby může docházet k přechodnému selhání, jako je vypršení časového limitu sítě. Tyto chyby se často dají vyřešit jednoduše opakováním volání. Pokud operace stále selže i po určitém počtu pokusů, považuje se za nepřesnou chybu.

  • Nepřestupné selhání je jakékoli selhání, které je nepravděpodobné, že zmizí samo o sobě. Mezi nepřestupná selhání patří normální chybové stavy, například neplatný vstup. Zahrnují také neošetřené výjimky v kódu aplikace nebo chybové ukončení procesu. Pokud dojde k tomuto typu chyby, musí být celá obchodní transakce označena jako selhání. Může být nutné vrátit zpět další kroky ve stejné transakci, které již proběhly úspěšně.

Po nepřestupném selhání může být aktuální transakce v částečně neúspěšném stavu, kde jeden nebo více kroků již úspěšně dokončeno. Pokud už například služba Drone naplánovala dron, musí být dron zrušen. V takovém případě musí aplikace vrátit zpět kroky, které byly úspěšné, pomocí kompenzační transakce. V některých případech to musí být provedeno externím systémem nebo dokonce ručním procesem.

Pokud je logika kompenzačních transakcí složitá, zvažte vytvoření samostatné služby, která je za tento proces zodpovědná. V aplikaci Pro doručování pomocí dronů služba Scheduler vkládá neúspěšné operace do vyhrazené fronty. Samostatná mikroslužba s názvem Supervisor čte z této fronty a volá rozhraní API pro zrušení ve službách, které potřebují kompenzaci. Toto je varianta vzoru Správce agenta plánovače. Služba Supervisor může provádět i další akce, například upozornit uživatele textovou nebo e-mailovou zprávou nebo odeslat upozornění na řídicí panel operací.

Diagram znázorňující mikroslužbu Supervisor

Samotná služba Scheduler může selhat (například kvůli chybovému ukončení uzlu). V takovém případě se může spustit a převzít nová instance. Všechny transakce, které již probíhaly, však musí být obnoveny.

Jedním z přístupů je uložení kontrolního bodu do odolného úložiště po dokončení každého kroku pracovního postupu. Pokud dojde k chybovému ukončení instance služby Scheduler uprostřed transakce, může nová instance pomocí kontrolního bodu pokračovat tam, kde předchozí instance skončila. Při psaní kontrolních bodů ale může vzniknout režijní náklady na výkon.

Další možností je navrhnout všechny operace tak, aby byly idempotentní. Operace je idempotentní, pokud ji lze volat vícekrát, aniž by po prvním volání došlo k dalším vedlejším účinkům. Podřízená služba by v podstatě měla ignorovat duplicitní volání, což znamená, že služba musí být schopná rozpoznat duplicitní volání. Implementace metod idempotentní není vždy jednoduchá. Další informace najdete v tématu Idempotentní operace.

Další kroky

U mikroslužeb, které spolu komunikují přímo, je důležité vytvořit dobře navržená rozhraní API.