Akka.Net

Opschalen met Akka.Net

​Als de hoeveelheid data, gebruikers of netwerkverkeer naar een applicatie stijgt en het einde van de capaciteit is in zicht, dan wordt het tijd om op te schalen. Dat kan met “scaling up”, meer geheugen of snellere processoren, of “scaling out”, waarbij een volledige extra machine die extra capaciteit moet geven. Omdat individuele processoren niet meer sneller worden, is multi-core en clustering de manier om meer capaciteit te krijgen.

Echter, de meeste software haalt nog lang niet het maximale uit één machine. Het framework “Akka.Net” helpt om dit beter te doen. Akka.Net is gebaseerd op “Akka” uit de Java wereld, geniet actieve steun van Microsoft en wordt Open Source aangeboden. Akka.Net is bedoeld voor de backend.

Actor model

Akka.Net implementeert het zogenaamde “Actor model”. Actors zijn een soort “live-objecten”, die in het geheugen blijven (idle of actief). Door een “message” te sturen naar een actor, kun je als het ware een “functie” op die actor aanroepen.

In de klassieke OO manier wordt data eerst uit een Database gehaald, vertaald naar een object. Vervolgens vinden data-mutaties plaats op het object en hierna wordt de data weer weggeschreven naar de Database.

In het Actor model staat de data al in het geheugen, worden mutaties op de Actor gedaan en daarna kan de data worden weggeschreven naar een database. In het actor model is database opslag eigenlijk een (optionele) feature.

De Actor is van zichzelf dus al een soort cache. Maar er zit meer aan vast. Want je kunt in een actor-system verschillende instanties maken van dezelfde actor en je kunt moeiteloos verschillende nodes in een cluster aan elkaar koppelen, met allerlei actors die met elkaar samenwerken.
Een actor is dus een bouwsteen, maar voor welke toepassing? Je kunt een Actor op een technische en op een functionele manier toepassen.

Op een functionele manier kun je een user-actor maken, die alles bijhoudt van één user, zijn profiel, zijn user-data enzovoort. Zo’n user-actor kan worden gedraaid op een node in een cluster. Op die manier kun je alle users op verschillende nodes in zo’n cluster draaien. Voeg je een nieuwe node toe aan de cluster, dan kun je die user-actors opnieuw spreiden over alle nodes. Mochten er mutaties plaatsvinden op die user-data, dan worden messages, één-voor-één in volgorde afgehandeld. Dus Akka.Net lost daarmee ook een distributed concurrency-probleem op.

Op een technische manier kan een Actor bijvoorbeeld als cache fungeren voor bijvoorbeeld een veel gestelde zoekopdracht op een website. Stel een node heeft 8 cores. Als je dan 8 keer dezelfde actor met die populaire zoekopdracht op die node draait, dan kan die zoekopdracht 8 keer concurrent worden uitgevoerd. Heb je twee machines met ieder 8 cores, kun je 16 actors aanmaken, 8 per node, waarbij een router-actor als portaal werkt om een ingekomen message slim door te sturen naar één van de zoekopdracht-actors die het werk doet. Je kunt ook een top-tien maken van meest gestelde zoekopdrachten, en per rang 8 actors draaien, op iedere node, zodat er altijd 8 concurrent zoekopdrachten uitgevoerd kunnen worden. Voor weinig gestelde zoekopdrachten kan men dan nog steeds direct naar de database gaan, die dan al behoorlijk ontlast is van de populaire zoekopdrachten.

Een actor is een speciaal soort object, hij heeft state/data en behavior  – de functie die de actor kan uitvoeren. Iedere actor heeft een eigen queue, waarop de messages binnenkomen, hiermee is er een garantie van volgorde waarin messages worden afgehandeld (lock in C# heeft kent deze garantie niet). Een actor kent “isolation”, dat wil zeggen dat iedere message geheel wordt afgehandeld, voordat de volgende message aan de beurt is. Ook heeft iedere actor een supervisor, met programmeerbaar gedrag voor als het misgaat in een child-actor.

Berichtenverkeer

Als je met Akka.Net aan de slag gaat, dan komt er een nieuwe “dimensie” bij, namelijk berichtenverkeer. Neem “verkeer” letterlijk. Speciale actors, “route-actors” zorgen bijvoorbeeld voor handige functies. Als bijv. een message een veld “userid” heeft, kun je ervoor zorgen dat alle messages met dezelfde “userid” naar dezelfde user-actor wordt doorgestuurd. Als je 8 actors hebt die een populaire zoekopdracht uitvoeren, kun je via een router-actor een message doorturen naar één van die 8 actors, en dan degene met de kortste queue. Of die gekozen actor lokaal of remote staat maakt voor de router dan niets uit.

Mocht je niet geïnteresseerd zijn in performance, dan biedt het actor model met Akka.Net nog de mogelijkheid tot micro-services. Omdat de messages tussen actors gebouwd worden met POCO’s, kun je een hele microservice implementeren in één actor. Voor onderlinge communicatie tussen microservices geeft dit al een stuk minder overhead, vergeleken met een Soap of Rest oplossing.

Akka.Net wordt aangeboden door “Petabridge” (https://petabridge.com/), is te verkrijgen via oa. nuget, en ondersteunt zowel C# als F#. Documentatie is te vinden via http://getakka.net/ . Zelf spreekt Petabridge over tests op een 48-core systeem, die 1 miljoen messages per seconde afhandelt. Lees ook het “Reactive Manifesto” (http://www.reactivemanifesto.org/ ) die een moderne omschrijving geeft van softwarekwaliteit.

Frank Joppe, developer