Minimaliseer de coördinatie
Coördinatie tussen toepassingsservices minimaliseren om schaalbaarheid te realiseren
De meeste cloudtoepassingen bestaan uit verschillende toepassingsservices — front-ends voor het web, databases, bedrijfsprocessen, rapportage en analyse, enzovoort. Teneinde schaalbaarheid en betrouwbaarheid te kunnen bieden, moet elk van deze services worden uitgevoerd op meerdere exemplaren.
Wat gebeurt er wanneer twee exemplaren gelijktijdige bewerkingen proberen uit te voeren die van invloed zijn op een gedeelde status? In sommige gevallen is coördinatie tussen knooppunten nodig, bijvoorbeeld voor het behouden van ACID-garanties. In dit diagram wacht Node2 totdat Node1 een databasevergrendeling heeft vrijgegeven:
Coördinatie beperkt de voordelen van horizontaal schalen en veroorzaakt knelpunten. Als u in dit voorbeeld de toepassing gaat uitschalen en dus meer exemplaren gaat toevoegen, zult u zien dat er vaker vergrendelingsconflicten optreden. In het ergste geval wachten de exemplaren van de front-end het merendeel van de tijd op het vrijgeven van vergrendelingen.
Semantiek op basis van 'exact één keer' is een andere veelvoorkomende oorzaak van coördinatie. Stel dat een order exact één keer moet worden verwerkt. Twee werkrollen luisteren naar nieuwe orders. Worker1 selecteert een order voor verwerking. De toepassing moet er dan voor zorgen dat Worker2 het werk niet dupliceert, maar ook dat de order wordt overgenomen als Worker1 uitvalt.
U kunt een patroon zoals Scheduler Agent Supervisor gebruiken voor de coördinatie tussen de werkrollen, maar in dit geval is het partitioneren van het werk waarschijnlijk een betere aanpak. Elke werkrol krijgt een bepaalde reeks orders toegewezen (bijvoorbeeld per factureringsregio). Als een werkrol uitvalt, wordt het werk door een nieuw exemplaar overgenomen op de positie waar het vorige exemplaar is afgebroken. Hierbij is echter geen sprake van conflicten tussen exemplaren.
Aanbevelingen
Uiteindelijke consistentie omarmen. Wanneer gegevens worden gedistribueerd, is coördinatie nodig voor het afdwingen van een gegarandeerde consistentie. Stel dat met een bewerking twee databases worden bijgewerkt. In plaats van hiervoor één transactiebereik te gebruiken, is het beter als het systeem uiteindelijke consistentie kan aanbieden, mogelijk door met behulp van het patroon Compenserende transactie een logisch herstelbewerking uit te voeren na een storing.
Gebruik domeingebeurtenissen om de status te synchroniseren. Een domeingebeurtenis is een gebeurtenis die wordt vastgelegd wanneer er iets gebeurt dat relevant is binnen het domein. Betrokken services kunnen luisteren naar de gebeurtenis, in plaats van een algemene transactie te gebruiken voor coördinatie van meerdere services. Als deze aanpak wordt gebruikt, moet het systeem uiteindelijke consistentie tolereren (zie het vorige item).
Houd rekening met patronen zoals CQRS en gebeurtenissourcing. Deze twee patronen kunnen helpen om de kans op conflicten tussen werkbelastingen voor lezen en voor schrijven te verkleinen.
Het CQRS-patroon scheidt leesbewerkingen van schrijfbewerkingen. In sommige implementaties worden de leesgegevens fysiek gescheiden van de schrijfgegevens.
In het patroon Gebeurtenisbronnen worden statuswijzigingen als een reeks gebeurtenissen vastgelegd in een gegevensarchief dat alleen toevoegen toestaat. Het toevoegen van een gebeurtenis aan de stroom is een atomische bewerking, waarvoor minimale vergrendeling vereist is.
Deze twee patronen vullen elkaar aan. Als het archief voor alleen schrijven in CQRS gebeurtenisbronnen gebruikt, kan het archief voor alleen lezen naar dezelfde gebeurtenissen luisteren om een leesbare momentopname van de huidige status te maken die is geoptimaliseerd voor query's. Voordat u kiest voor CQRS of gebeurtenisbronnen is het belangrijk om meer te weten over de uitdagingen die deze benadering met zich meebrengt.
Gegevens partitioneren. Plaats niet alle gegevens in één gegevensschema dat wordt gedeeld met een groot aantal toepassingsservices. In een architectuur van microservices wordt dit principe afgedwongen door elke service verantwoordelijk te maken voor het eigen gegevensarchief. Binnen een individuele database kunt u de gegevens partitioneren in shards om de gelijktijdigheid van taken te verbeteren. Een service die gegevens schrijft naar de ene shard-service heeft immers geen invloed op een service die gegevens naar een andere shard-service schrijft.
idempotente bewerkingen ontwerpen. Ontwerp bewerkingen indien mogelijk zo dat ze idempotent zijn. Op die manier kunnen ze worden afgehandeld via semantiek op basis van 'exact één keer'. U kunt werkitems bijvoorbeeld in een wachtrij zetten. Als een werkrol midden in een bewerking uitvalt, wordt het werkitem overgenomen door een andere werknemer.
Gebruik asynchrone parallelle verwerking. Als een bewerking meerdere stappen vereist die asynchroon worden uitgevoerd (zoals aanroepen van externe services), kunt u deze mogelijk parallel aanroepen en de resultaten vervolgens samenvoegen. Hierbij wordt ervan uitgegaan dat geen enkele stap afhankelijk is van de resultaten van de vorige stap.
Gebruik waar mogelijk optimistische gelijktijdigheid. Bij controle op basis van pessimistische gelijktijdigheid worden databasevergrendelingen gebruikt om conflicten te voorkomen. Dit kan gevolgen hebben voor de prestaties en de beschikbaarheid verminderen. Bij controle op basis van optimistische gelijktijdigheid wordt met elke transactie een kopie of momentopname van de gegevens gewijzigd. Voordat de transactie wordt doorgevoerd, wordt deze gecontroleerd door de database-engine. Transacties die van invloed zijn op de consistentie van de database worden geweigerd.
Azure SQL Database en SQL Server ondersteunen optimistische gelijktijdigheid via de isolatie van momentopnamen. Sommige services van Azure Storage ondersteunen optimistische gelijktijdigheid via het gebruik van Etags, zoals Azure Cosmos DB en Azure Storage.
Overweeg MapReduce of andere parallelle, gedistribueerde algoritmen. Afhankelijk van de gegevens en het type werk dat moet worden uitgevoerd, kunt u het werk mogelijk opsplitsen in onafhankelijke taken die kunnen worden uitgevoerd door meerdere knooppunten die parallel werken. Zie Architectuurstijl: Big Compute voor meer informatie.
Kies leider voor coördinatie. In situaties waarin u bewerkingen moet coördineren, is het belangrijk dat de coördinator niet een Single Point of Failure wordt in de toepassing. Als u het patroon Selectie van leider gebruikt, is er altijd maar één exemplaar de leider op een gegeven moment en fungeert deze als de coördinator. Als de leider uitvalt, wordt er een nieuw exemplaar gekozen als de leider.