Coördinatie minimaliseren om schaalbaarheid te bereiken
De meeste cloudtoepassingen bestaan uit meerdere toepassingsservices: web-front-ends, databases, bedrijfsprocessen, rapportage en analyse, enzovoort. Teneinde schaalbaarheid en betrouwbaarheid te kunnen bieden, moet elk van deze services worden uitgevoerd op meerdere exemplaren.
Niet-gecoördineerde systemen, waar werk onafhankelijk kan worden afgehandeld zonder dat berichten tussen machines hoeven te worden doorgegeven, zijn over het algemeen eenvoudiger te schalen. Coördinatie is meestal geen binaire status, maar een spectrum. Coördinatie vindt plaats op verschillende lagen, zoals gegevens of berekeningen.
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
Losgekoppelde onderdelen gebruiken die asynchroon communiceren. Onderdelen moeten in het ideale geval gebeurtenissen gebruiken om met elkaar te communiceren.
Uiteindelijke consistentie is het streven. 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).
Overweeg patronen zoals CQRS en Gebeurtenisbronnen. 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.
Partitiegegevens en status. 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. Hoewel partitionering enige mate van coördinatie toevoegt, kunt u partitionering gebruiken om parallellisme te verhogen voor betere schaalbaarheid. Partitie monolithische status in kleinere segmenten, zodat de gegevens onafhankelijk kunnen worden beheerd.
Ontwerp idempotente bewerkingen. 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. Als de werkrol gegevens moet bijwerken en andere berichten als onderdeel van de logica moet verzenden, moet het idempotente berichtverwerkingspatroon worden gebruikt.
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 het gebruik van 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.
Gebruik het patroon Selectie van 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.