Inleiding tot Service Fabric Reliable Actors
Reliable Actors is een Service Fabric-toepassingsframework op basis van het virtual actor-patroon . De Reliable Actors-API biedt een programmeermodel met één thread dat is gebaseerd op de schaalbaarheids- en betrouwbaarheidsgaranties van Service Fabric.
Wat zijn acteurs?
Een actor is een geïsoleerde, onafhankelijke reken- en statuseenheid met uitvoering met één thread. Het actorpatroon is een rekenmodel voor gelijktijdige of gedistribueerde systemen waarin een groot aantal van deze actoren gelijktijdig en onafhankelijk van elkaar kan worden uitgevoerd. Actoren kunnen met elkaar communiceren en ze kunnen meer actoren maken.
Wanneer gebruikt u Reliable Actors?
Service Fabric Reliable Actors is een implementatie van het ontwerppatroon van de actor. Net als bij elk softwareontwerppatroon wordt de beslissing genomen of een specifiek patroon moet worden gebruikt op basis van of een softwareontwerpprobleem al dan niet past bij het patroon.
Hoewel het ontwerppatroon van de actor geschikt kan zijn voor een aantal problemen en scenario's met gedistribueerde systemen, moet u zorgvuldig rekening houden met de beperkingen van het patroon en het framework dat het implementeert. Als algemene richtlijnen kunt u het actorpatroon overwegen om uw probleem of scenario te modelleren als:
- Uw probleemruimte omvat een groot aantal (duizenden of meer) kleine, onafhankelijke en geïsoleerde eenheden van status en logica.
- U wilt werken met objecten met één thread die geen significante interactie van externe onderdelen vereisen, waaronder het uitvoeren van querystatussen in een set actoren.
- Uw actorinstanties blokkeren bellers met onvoorspelbare vertragingen niet door I/O-bewerkingen uit te geven.
Actoren in Service Fabric
In Service Fabric worden actoren geïmplementeerd in het Reliable Actors-framework: een toepassingsframework op basis van actorpatronen dat is gebaseerd op Service Fabric Reliable Services. Elke Reliable Actor-service die u schrijft, is eigenlijk een gepartitioneerde, stateful Reliable Service.
Elke actor wordt gedefinieerd als een exemplaar van een actortype, identiek aan de manier waarop een .NET-object een exemplaar van een .NET-type is. Er kan bijvoorbeeld een actortype zijn dat de functionaliteit van een calculator implementeert en er kunnen veel actoren van dat type zijn die op verschillende knooppunten in een cluster worden gedistribueerd. Elke dergelijke actor wordt uniek geïdentificeerd door een actor-id.
Levensduur van actor
Service Fabric-actoren zijn virtueel, wat betekent dat hun levensduur niet is gekoppeld aan hun in-memory representatie. Als gevolg hiervan hoeven ze niet expliciet te worden gemaakt of vernietigd. De Reliable Actors-runtime activeert automatisch een actor wanneer deze de eerste keer een aanvraag voor die actor-id ontvangt. Als een actor gedurende een bepaalde periode niet wordt gebruikt, verzamelt de Reliable Actors-runtime het in-memory object. Het behoudt ook de kennis van het bestaan van de actor indien deze later opnieuw moet worden geactiveerd. Zie actorlevenscyclus en garbagecollection voor meer informatie.
Deze levensduurabstractie van de virtuele actor draagt enkele opmerkingen als gevolg van het virtuele actormodel, en in feite wijkt de implementatie van Reliable Actors af op momenten van dit model.
- Een actor wordt automatisch geactiveerd (waardoor een actorobject wordt samengesteld) wanneer een bericht voor het eerst naar de actor-id wordt verzonden. Na enige tijd wordt het actorobject garbage verzameld. In de toekomst wordt met behulp van de actor-id opnieuw een nieuw actorobject gemaakt. De status van een actor overleeft de levensduur van het object wanneer het wordt opgeslagen in de statusbeheerder.
- Als u een actormethode aanroept voor een actor-id, wordt die actor geactiveerd. Daarom hebben actortypen impliciet hun constructor die door de runtime wordt aangeroepen. Clientcode kan daarom geen parameters doorgeven aan de constructor van het actortype, hoewel parameters door de service zelf aan de constructor van de actor kunnen worden doorgegeven. Het resultaat is dat actoren kunnen worden samengesteld in een gedeeltelijk geïnitialiseerde status op het moment dat andere methoden worden aangeroepen, als de actor initialisatieparameters van de client vereist. Er is geen enkel toegangspunt voor de activering van een actor van de client.
- Hoewel Reliable Actors impliciet actorobjecten maakt; U hebt de mogelijkheid om een actor en de status ervan expliciet te verwijderen.
Distributie en failover
Om schaalbaarheid en betrouwbaarheid te bieden, distribueert Service Fabric actoren in het hele cluster en migreert ze automatisch van mislukte knooppunten naar gezonde knooppunten, indien nodig. Dit is een abstractie van een gepartitioneerde, stateful Reliable Service. Distributie, schaalbaarheid, betrouwbaarheid en automatische failover worden allemaal geboden door het feit dat actoren worden uitgevoerd in een stateful Reliable Service, de Actor-service.
Actoren worden verdeeld over de partities van de Actor-service en deze partities worden verdeeld over de knooppunten in een Service Fabric-cluster. Elke servicepartitie bevat een set actoren. Service Fabric beheert de distributie en failover van de servicepartities.
Een actorservice met negen partities die zijn geïmplementeerd op drie knooppunten met behulp van de standaardplaatsing van actorpartitie, wordt dus gedistribueerd:
Het Actor Framework beheert de partitieschema- en sleutelbereikinstellingen voor u. Dit vereenvoudigt enkele keuzes, maar houdt ook rekening met het volgende:
- Met Reliable Services kunt u een partitioneringsschema, sleutelbereik (wanneer u een bereikpartitioneringsschema gebruikt) en het aantal partities kiezen. Reliable Actors is beperkt tot het bereikpartitioneringsschema (het uniforme Int64-schema) en vereist dat u het volledige Int64-sleutelbereik gebruikt.
- Standaard worden actoren willekeurig in partities geplaatst, wat resulteert in uniforme distributie.
- Omdat actoren willekeurig worden geplaatst, moet worden verwacht dat actorbewerkingen altijd netwerkcommunicatie vereisen, waaronder serialisatie en deserialisatie van methodeaanroepgegevens, waardoor latentie en overhead ontstaat.
- In geavanceerde scenario's is het mogelijk om de plaatsing van actorpartities te beheren met behulp van Int64 actor-id's die zijn toegewezen aan specifieke partities. Dit kan echter resulteren in een niet-verdeelde verdeling van actoren over partities.
Raadpleeg de partitioneringsconcepten voor actoren voor meer informatie over hoe actorservices worden gepartitioneerd.
Actorcommunicatie
Actorinteracties worden gedefinieerd in een interface die wordt gedeeld door de actor die de interface implementeert en de client die een proxy naar een actor krijgt via dezelfde interface. Omdat deze interface wordt gebruikt om actormethoden asynchroon aan te roepen, moet elke methode op de interface taakverzending zijn.
Methode-aanroepen en hun antwoorden resulteren uiteindelijk in netwerkaanvragen in het cluster, dus de argumenten en de resultaattypen van de taken die ze retourneren, moeten serialiseerbaar zijn voor het platform. Ze moeten met name serialiseerbaar zijn voor gegevenscontracten.
De actorproxy
De Reliable Actors-client-API biedt communicatie tussen een actorinstantie en een actorclient. Voor communicatie met een actor maakt een client een actorproxyobject dat de actorinterface implementeert. De client communiceert met de actor door methoden voor het proxyobject aan te roepen. De actorproxy kan worden gebruikt voor communicatie tussen clients en actor-naar-actor.
// Create a randomly distributed actor ID
ActorId actorId = ActorId.CreateRandom();
// This only creates a proxy object, it does not activate an actor or invoke any methods yet.
IMyActor myActor = ActorProxy.Create<IMyActor>(actorId, new Uri("fabric:/MyApp/MyActorService"));
// This will invoke a method on the actor. If an actor with the given ID does not exist, it will be activated by this method call.
await myActor.DoWorkAsync();
// Create actor ID with some name
ActorId actorId = new ActorId("Actor1");
// This only creates a proxy object, it does not activate an actor or invoke any methods yet.
MyActor myActor = ActorProxyBase.create(actorId, new URI("fabric:/MyApp/MyActorService"), MyActor.class);
// This will invoke a method on the actor. If an actor with the given ID does not exist, it will be activated by this method call.
myActor.DoWorkAsync().get();
Houd er rekening mee dat de twee gegevens die worden gebruikt om het actorproxyobject te maken, de actor-id en de naam van de toepassing zijn. De actor-id identificeert de actor op unieke wijze, terwijl de naam van de toepassing de Service Fabric-toepassing identificeert waar de actor wordt geïmplementeerd.
De ActorProxy
klasse (C#) / ActorProxyBase
(Java) aan de clientzijde voert de benodigde resolutie uit om de actor op id te zoeken en er een communicatiekanaal mee te openen. Ook wordt opnieuw geprobeerd om de actor te vinden in het geval van communicatiefouten en failovers. Als gevolg hiervan heeft de bezorging van berichten de volgende kenmerken:
- Berichtbezorging is het beste.
- Actoren ontvangen mogelijk dubbele berichten van dezelfde client.
Gelijktijdigheid
De Reliable Actors-runtime biedt een eenvoudig op turn-based toegangsmodel voor toegang tot actormethoden. Dit betekent dat niet meer dan één thread op elk gewenst moment actief kan zijn in de code van een actorobject. Turn-based access vereenvoudigt aanzienlijk gelijktijdige systemen omdat er geen synchronisatiemechanismen nodig zijn voor gegevenstoegang. Het betekent ook dat systemen moeten worden ontworpen met speciale overwegingen voor de toegang tot één thread van elke actorinstantie.
- Eén actorinstantie kan niet meer dan één aanvraag tegelijk verwerken. Een actorexemplaren kunnen een knelpunt voor doorvoer veroorzaken als er naar verwachting gelijktijdige aanvragen worden verwerkt.
- Actoren kunnen elkaar blokkeren als er een kringaanvraag tussen twee actoren is, terwijl er tegelijkertijd een externe aanvraag wordt ingediend bij een van de actoren. De actorruntime treedt automatisch een time-out op bij actoraanroepen en genereert een uitzondering op de beller om mogelijke impassesituaties te onderbreken.
Toegang op basis van turn-based
Een beurt bestaat uit de volledige uitvoering van een actormethode als reactie op een aanvraag van andere actoren of clients, of de volledige uitvoering van een callback van een timer/herinnering . Hoewel deze methoden en callbacks asynchroon zijn, interleave de Actors-runtime ze niet. Een draai moet volledig zijn voltooid voordat een nieuwe beurt is toegestaan. Met andere woorden, een actormethode of timer/herinnering callback die momenteel wordt uitgevoerd, moet volledig zijn voltooid voordat een nieuwe aanroep naar een methode of callback is toegestaan. Een methode of callback wordt beschouwd als voltooid als de uitvoering is geretourneerd uit de methode of callback en de taak die wordt geretourneerd door de methode of callback is voltooid. Het is de moeite waard om te benadrukken dat gelijktijdigheid op basis van turn-based wordt gerespecteerd, zelfs op verschillende methoden, timers en callbacks.
De Actors-runtime dwingt gelijktijdigheid op basis van turn-based af door aan het begin van een draai een vergrendeling per actor te verkrijgen en de vergrendeling aan het einde van de beurt los te laten. Op basis van turn-based gelijktijdigheid wordt dus afgedwongen per actor en niet tussen actoren. Actormethoden en timer/herinnering callbacks kunnen gelijktijdig worden uitgevoerd namens verschillende actoren.
In het volgende voorbeeld ziet u de bovenstaande concepten. Overweeg een actortype waarmee twee asynchrone methoden (bijvoorbeeld Method1 en Method2), een timer en een herinnering worden geïmplementeerd. In het onderstaande diagram ziet u een voorbeeld van een tijdlijn voor de uitvoering van deze methoden en callbacks namens twee actoren (ActorId1 en ActorId2) die deel uitmaken van dit actortype.
Dit diagram volgt de volgende conventies:
- Elke verticale lijn toont de logische stroom van de uitvoering van een methode of een callback namens een bepaalde actor.
- De gebeurtenissen die op elke verticale lijn zijn gemarkeerd, vinden plaats in chronologische volgorde, waarbij nieuwere gebeurtenissen zich onder oudere gebeurtenissen voordoen.
- Verschillende kleuren worden gebruikt voor tijdlijnen die overeenkomen met verschillende actoren.
- Markering wordt gebruikt om de duur aan te geven waarvoor de vergrendeling per actor wordt gehouden namens een methode of callback.
Enkele belangrijke punten om rekening mee te houden:
- Hoewel Method1 wordt uitgevoerd namens ActorId2 als reactie op de clientaanvraag xyz789, komt een andere clientaanvraag (abc123) binnen waarvoor ook Method1 moet worden uitgevoerd door ActorId2. De tweede uitvoering van Methode1 begint echter pas als de vorige uitvoering is voltooid. Op dezelfde manier wordt een herinnering die is geregistreerd door ActorId2 geactiveerd terwijl Method1 wordt uitgevoerd als reactie op de clientaanvraag xyz789. De callback van de herinnering wordt pas uitgevoerd nadat beide uitvoeringen van Method1 zijn voltooid. Dit komt doordat gelijktijdigheid op basis van turn-based wordt afgedwongen voor ActorId2.
- Op dezelfde manier wordt op turn-based gelijktijdigheid ook afgedwongen voor ActorId1, zoals wordt aangetoond door de uitvoering van Method1, Method2 en de timer callback namens ActorId1 op seriële wijze.
- De uitvoering van Method1 namens ActorId1 overlapt met de uitvoering namens ActorId2. Dit komt doordat gelijktijdigheid op basis van turn-based alleen binnen een actor en niet tussen actoren wordt afgedwongen.
- In sommige van de uitvoeringen van de methode/callback wordt de
Task
(C#) /CompletableFuture
(Java) geretourneerd door de methode/callback voltooid nadat de methode is geretourneerd. In sommige andere gevallen is de asynchrone bewerking al voltooid op het moment dat de methode/callback wordt geretourneerd. In beide gevallen wordt de vergrendeling per actor pas vrijgegeven nadat zowel de methode/callback wordt geretourneerd als de asynchrone bewerking is voltooid.
Herintreding
De Actors-runtime staat standaard reentrancy toe. Dit betekent dat als een actormethode van Actor A een methode aanroept op Actor B, die op zijn beurt een andere methode aanroept op Actor A, die methode mag worden uitgevoerd. Dit komt doordat deze deel uitmaakt van dezelfde logische context van de aanroepketen. Alle timer- en herinneringsgesprekken beginnen met de nieuwe logische oproepcontext. Zie de Reliable Actors-reentrancy voor meer informatie.
Bereik van gelijktijdigheidsgaranties
De Actors-runtime biedt deze gelijktijdigheidsgaranties in situaties waarin de aanroep van deze methoden wordt beheerd. Het biedt bijvoorbeeld deze garanties voor de methodeaanroepen die worden uitgevoerd als reactie op een clientaanvraag, evenals voor callbacks voor timer en herinnering. Als de actorcode deze methoden echter rechtstreeks aanroept buiten de mechanismen van de Actors-runtime, kan de runtime geen gelijktijdigheidsgaranties bieden. Als de methode bijvoorbeeld wordt aangeroepen in de context van een taak die niet is gekoppeld aan de taak die door de actormethoden wordt geretourneerd, kan de runtime geen gelijktijdigheidsgaranties bieden. Als de methode wordt aangeroepen vanuit een thread die de actor zelf maakt, kan de runtime ook geen gelijktijdigheidsgaranties bieden. Om achtergrondbewerkingen uit te voeren, moeten acteurs timers en actorherinneringen gebruiken die rekening moeten hebben met op turn gebaseerde gelijktijdigheid.
Volgende stappen
Ga aan de slag door uw eerste Reliable Actors-service te bouwen: