Inleiding tot Betrouwbare actoren van Service Fabric

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 garanties voor schaalbaarheid en betrouwbaarheid 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 rekenkundig model 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 creëren.

Wanneer gebruikt u Reliable Actors?

Service Fabric Reliable Actors is een implementatie van het ontwerppatroon actor. Net als bij elk softwareontwerppatroon wordt de beslissing of u een specifiek patroon wilt gebruiken gemaakt op basis van het feit of een softwareontwerpprobleem al dan niet past bij het patroon.

Hoewel het ontwerppatroon van de actor goed kan passen bij een aantal problemen en scenario's met gedistribueerde systemen, moet zorgvuldig rekening worden gehouden met de beperkingen van het patroon en het framework dat het implementeert. Als algemene richtlijn 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 query's op de status van een set actoren.
  • Uw actorexemplaren blokkeren aanroepers met onvoorspelbare vertragingen niet door het uitvoeren van I/O-bewerkingen.

Actoren in Service Fabric

In Service Fabric worden actoren geïmplementeerd in het Reliable Actors-framework: een toepassingsframework op basis van een actorpatroon dat is gebouwd op Service Fabric Reliable Services. Elke Reliable Actor-service die u schrijft, is in feite een gepartitioneerde, stateful Reliable Service.

Elke actor wordt gedefinieerd als een instantie 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 rekenmachine implementeert en er kunnen veel actoren van dat type zijn die worden gedistribueerd op verschillende knooppunten in een cluster. 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 weergave in het geheugen. Als gevolg hiervan hoeven ze niet expliciet te worden gemaakt of vernietigd. De Reliable Actors-runtime activeert automatisch een actor wanneer deze voor het eerst een aanvraag voor die actor-id ontvangt. Als een actor gedurende een bepaalde periode niet wordt gebruikt, verzamelt de Reliable Actors runtime garbage-garbage het object in het geheugen. Het behoudt ook kennis van het bestaan van de actor als deze later opnieuw moet worden geactiveerd. Zie Actor lifecycle and garbagecollection (Levenscyclus van actor en garbagecollection) voor meer informatie.

Deze levensduurabstractie van de virtuele actor heeft enkele kanttekeningen als gevolg van het virtuele actormodel, en in feite wijkt de implementatie van Reliable Actors soms af van dit model.

  • Een actor wordt automatisch geactiveerd (waardoor een actorobject wordt gemaakt) de eerste keer dat een bericht naar de actor-id wordt verzonden. Na enige tijd wordt het actorobject afval verzameld. Als u in de toekomst opnieuw de actor-id gebruikt, wordt er een nieuw actorobject gemaakt. De status van een actor overleeft de levensduur van het object wanneer deze is opgeslagen in de statusmanager.
  • Het aanroepen van een actormethode voor een actor-id activeert die actor. Daarom hebben actortypen hun constructor impliciet aangeroepen door de runtime. Daarom kan de clientcode geen parameters doorgeven aan de constructor van het actortype, hoewel parameters door de service zelf kunnen worden doorgegeven aan de constructor van de actor. Het resultaat is dat actoren in een gedeeltelijk geïnitialiseerde status kunnen worden gemaakt 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 maken; U hebt de mogelijkheid om een actor en de bijbehorende status expliciet te verwijderen.

Distributie en failover

Om schaalbaarheid en betrouwbaarheid te bieden, distribueert Service Fabric actoren in het hele cluster en migreert deze indien nodig automatisch van mislukte knooppunten naar gezonde knooppunten. 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 binnen 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 standaard-actorpartitieplaatsing, wordt bijvoorbeeld als volgt gedistribueerd:

Reliable Actors-distributie

Het Actor Framework beheert de partitieschema- en sleutelbereikinstellingen voor u. Dit vereenvoudigt sommige keuzes, maar houdt ook rekening met:

  • Met Reliable Services kunt u een partitieschema, 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 een uniforme verdeling.
  • Omdat actoren willekeurig worden geplaatst, moet worden verwacht dat actorbewerkingen altijd netwerkcommunicatie vereisen, inclusief serialisatie en deserialisatie van aanroepgegevens van methoden, met latentie en overhead.
  • 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 leiden tot een onevenwichtige verdeling van actoren over partities.

Raadpleeg 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 via dezelfde interface een proxy naar een actor krijgt. Omdat deze interface wordt gebruikt om actormethoden asynchroon aan te roepen, moet elke methode op de interface Taak retourneren.

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 door het platform. In het bijzonder moeten ze serialiseerbaar zijn voor gegevenscontracten.

De actorproxy

De Reliable Actors-client-API biedt communicatie tussen een actorexemplaar en een actorclient. Om met een actor te communiceren, maakt een client een actorproxyobject dat de actorinterface implementeert. De client communiceert met de actor door methoden aan te roepen voor het proxyobject. De actorproxy kan worden gebruikt voor communicatie van client naar actor 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 ActorProxyklasse (C#) / ActorProxyBase(Java) aan de clientzijde voert de benodigde resolutie uit om de actor op id te vinden en hiermee een communicatiekanaal te openen. Er wordt ook 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 best effort.
  • Actoren kunnen dubbele berichten van dezelfde client ontvangen.

Gelijktijdigheid

De Reliable Actors-runtime biedt een eenvoudig turn-based toegangsmodel voor toegang tot actormethoden. Dit betekent dat er op elk gewenst moment niet meer dan één thread actief kan zijn in de code van een actorobject. Op turn gebaseerde toegang vereenvoudigt gelijktijdige systemen aanzienlijk, omdat er geen synchronisatiemechanismen nodig zijn voor gegevenstoegang. Dit betekent ook dat systemen moeten worden ontworpen met speciale overwegingen voor de aard van toegang met één thread van elk actorexemplaar.

  • Eén actorexemplaar kan niet meer dan één aanvraag tegelijk verwerken. Een actorexemplaar kan een knelpunt in de doorvoer veroorzaken als naar verwachting gelijktijdige aanvragen worden verwerkt.
  • Actoren kunnen elkaar vastlopen als er een circulaire aanvraag tussen twee actoren is, terwijl er tegelijkertijd een externe aanvraag wordt ingediend bij een van de actoren. De actorruntime treedt automatisch op bij actoraanroepen en genereert een uitzondering op de aanroeper om mogelijke impassesituaties te onderbreken.

Reliable Actors-communicatie

Turn-based toegang

Een beurt bestaat uit de volledige uitvoering van een actormethode als reactie op een verzoek van andere actoren of clients, of de volledige uitvoering van een timer/herinnering callback. Hoewel deze methoden en callbacks asynchroon zijn, worden ze niet door de Actors-runtime gecombineerd. Een bocht moet volledig zijn voltooid voordat een nieuwe bocht wordt toegestaan. Met andere woorden, een actormethode of callback van timer/herinnering 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 van de methode of callback is geretourneerd en de taak die door de methode of callback wordt geretourneerd, is voltooid. Het is de moeite waard om te benadrukken dat gelijktijdigheid op basis van turn wordt gerespecteerd, zelfs voor verschillende methoden, timers en callbacks.

De Actors-runtime dwingt gelijktijdigheid op basis van turn 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. Daarom wordt gelijktijdigheid op basis van turn afgedwongen per actor en niet tussen actoren. Actormethoden en callbacks voor timers/herinneringen kunnen tegelijkertijd namens verschillende actoren worden uitgevoerd.

In het volgende voorbeeld ziet u de bovenstaande concepten. Overweeg een actortype dat twee asynchrone methoden implementeert (bijvoorbeeld Methode1 en Methode2), een timer en een herinnering. In het onderstaande diagram ziet u een voorbeeld van een tijdlijn voor het uitvoeren van deze methoden en callbacks namens twee actoren (ActorId1 en ActorId2) die tot dit actortype behoren.

Turn-based gelijktijdigheid en toegang van Reliable Actors-runtime

Dit diagram volgt de volgende conventies:

  • Elke verticale lijn toont de logische uitvoeringsstroom 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 voordoen onder oudere.
  • Er worden verschillende kleuren gebruikt voor tijdlijnen die overeenkomen met verschillende actoren.
  • Markering wordt gebruikt om de duur aan te geven gedurende welke de vergrendeling per actor wordt gehouden namens een methode of callback.

Enkele belangrijke punten om rekening mee te houden:

  • Terwijl Method1 wordt uitgevoerd namens ActorId2 als reactie op clientaanvraag xyz789, komt er een andere clientaanvraag (abc123) binnen waarvoor ook Method1 moet worden uitgevoerd door ActorId2. De tweede uitvoering van Method1 begint echter pas nadat de vorige uitvoering is voltooid. Op dezelfde manier wordt een herinnering die is geregistreerd door ActorId2 geactiveerd terwijl Method1 wordt uitgevoerd als reactie op clientaanvraag xyz789. Het terugbellen van de herinnering wordt pas uitgevoerd nadat beide uitvoeringen van Method1 zijn voltooid. Dit komt allemaal doordat gelijktijdigheid op basis van turn wordt afgedwongen voor ActorId2.
  • Op dezelfde manier wordt gelijktijdigheid op basis van turn ook afgedwongen voor ActorId1, zoals blijkt uit de uitvoering van Method1, Method2 en de timer-callback namens ActorId1 die op seriële wijze plaatsvinden.
  • Uitvoering van Method1 namens ActorId1 overlapt met de uitvoering namens ActorId2. Dit komt doordat gelijktijdigheid op basis van turn alleen wordt afgedwongen binnen een actor en niet tussen actoren.
  • In sommige van de uitvoeringen van de methode/callback wordt de Task(C#) / CompletableFuture(Java) die door de methode/callback wordt geretourneerd, 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 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 op Actor A aanroept, die methode mag worden uitgevoerd. Dit komt omdat het deel uitmaakt van dezelfde logische aanroepketencontext. Alle timer- en herinneringsoproepen beginnen met de nieuwe logische oproepcontext. Zie reliable actors reentrancy (Reliable Actors reentrancy) voor meer informatie.

Bereik van gelijktijdigheidsgaranties

De Actors-runtime biedt deze gelijktijdigheidsgaranties in situaties waarin het aanroepen 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 timers en herinneringen. 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 actoren daarom timers voor acteurs en actorherinneringen gebruiken met inachtneming van gelijktijdigheid op basis van turn.

Volgende stappen

Ga aan de slag door uw eerste Reliable Actors-service te bouwen: