Reactive: het Akka framework

In een vorige blog gaven we reeds een uitvoerige inleiding van het “Reactive” paradigma. Vermits dit toch wel een belangrijke en invloedrijke zaak geworden is binnen de developer wereld, lijkt het ons nuttig om hier op terug te komen en wat dieper in te gaan op een voorbeeld van een Reactive framework: het Akka framework, één van de pioniers binnen de Reactive beweging.

Wat is Reactive alweer?

Reactieve systemen worden gezien als één van de belangrijkste ontwikkelingen om systemen responsiever te maken voor meer veeleisende eindgebruikers, en om overweg te kunnen met de grote hoeveelheid data die moderne systemen overspoelt. Zogenaamde “wearables” en andere IoT zaken kunnen bijvoorbeeld een constante stroom aan data genereren. Wanneer men dan de data van vele honderden van zulke devices moet verwerken, komen traditionele systemen vaak in de problemen. Maar ook voor ‘gewone’ toepassingen kan deze technologie heil brengen, doordat de responsiviteit en resiliëntie ook kunnen worden verhoogd.

Figuur 1: De vier basiseigenschappen van Reactieve systemen en hoe ze zich tot elkaar verhouden.

Aan de grondslag van Reactive ligt het feit dat het “message driven” is (Zie Fig. 1). Men zal een systeem bouwen aan de hand van een aantal met elkaar communicerende subsystemen die berichten zullen uitwisselen. Dit kan zowel rechtstreeks als via het publish/subscribe mechanisme van Events. Deze communicatie is altijd inherent asynchroon. Men weet niet wanneer er op een bericht zal worden gereageerd en men gaat er ook niet expliciet op wachten. Deze vorm van communicatie tussen componenten zorgt ervoor dat ze losjes gekoppeld zijn en locatietransparant. Dit “message driven” fundament leidt verder tot verschillende voordelen.

Eerst en vooral zorgt dit voor resiliëntie. Asynchrone communicatie laat toe om componenten veel onafhankelijker van elkaar te laten opereren dan synchrone communicatie, wat ervoor kan zorgen dat één component verder kan indien de andere (tijdelijk) faalt. De manier waarop berichten behandeld worden bij Reactive maakt ook van foutmeldingen gewone berichten; men behandelt deze als ‘first class citizen’, en niet zozeer als een uitzondering. Het omgaan met fouten en met niet-werkende componenten wordt dan als van nature mee opgenomen in het programmeren van zo’n systeem.

Ten tweede stijgt ook de elasticiteit. Bij reactieve systemen is de berichtenstroom heel belangrijk. Men kan deze gaan monitoren en ingrijpen wanneer deze sterk in volume toe- of afneemt, en snel reageren door meer componenten op te starten (of te stoppen), om deze berichten af te handelen. Ook het feit dat het systeem bestaat uit een heel aantal samenwerkende componenten, en niet afhankelijk is van een centrale bottleneck, komt de schaalbaarheid ten goede.

Ten slotte moet dit alles samen zorgen voor een verhoogde responsiviteit. Men zorgt ervoor dat men tijdig en vooral binnen een bepaalde tijd kan reageren op alle binnenkomende berichten, voor een consistente Quality of Service.

Het Akka raamwerk

Akka zag het licht in 2009 als één van de eerste raamwerken die het principe van het Actor programmeermodel naar een mainstream server programmeertaal bracht. Het is beschikbaar zowel voor de programmeertaal Scala (een taal die werkt op de JVM), waar het in principe voor is gebouwd, als voor Java (waar het iets meer boilerplate code vergt). Tegenwoordig maakt het deel uit van het Lightbend platform, samen met het Play raamwerk en de taal Scala.

Actoren kan men zien als kleine onafhankelijke machines binnen een softwaresysteem. Ze communiceren enkel met de buitenwereld (en dus met andere actors) via asynchrone berichten. Wanneer een actor een bericht krijgt, kan deze een aantal verschillende dingen doen: lokale code uitvoeren, zijn eigen toestand veranderen, zelf berichten uitsturen, andere actoren creëren, en beslissen hoe op het volgende bericht zal worden gereageerd. Een actor leeft dus volledig binnen zijn eigen executie-stack. Men kan deze dus beschouwen als een apart proces of een aparte thread, maar in een raamwerk als Akka is het mogelijk om deze veel efficiënter te instantiëren dan dat (zelfs threads zijn nog relatieve zwaargewichten, terwijl een actor in akka maar een paar honderd bytes hoeft in te nemen; één thread zal dan ook typisch vele actors tegelijk aansturen). Daarnaast kan men er voor zorgen dat een actor een andere actor als “supervisor” krijgt. Deze zal in het oog houden of de gesuperviseerde actor faalt, en kan dus indien nodig gepast reageren.

Eén van de meest interessante zaken aan actors is dat het voor de programmeur transparant kan gemaakt worden waar een actor zich bevindt: het kan een andere actor binnen hetzelfde proces zijn, een actor binnen een ander proces op dezelfde machine, of een actor aan de andere kant van het internet: de programmeur kan deze op dezelfde manier behandelen. Dit is één van de eigenschappen die Akka Cloud Native maakt: het is een technologie gebouwd vóór de Cloud, waarmee men optimaal kan gebruik maken van de eigenschappen van de Cloud.

Naast de ‘core’ Akka bibliotheek, bestaan er ook nog heel wat modulaire uitbreidingen, zoals b.v. voor het werken met streams, persistentie, webservers en http, en allerlei integraties, zoals b.v. met het Play framework, of met Apache Kafka. Ook RESTful APIs worden ondersteund.

Een voorbeeld

In de code duiken zou een beetje te technisch worden, maar we kunnen toch illustreren wat men met deze manier van programmeren kan bereiken aan de hand van een eenvoudig voorbeeld.

In onderstaande figuur zie je een tekening van een aantal actoren in een boekenwinkel systeem en de berichten die ze elkaar sturen: er zijn klanten, de boekenwinkel zelf, en bedienden (die boeken opsturen). Het systeem zal als volgt werken: klanten sturen bestellingen naar de winkel; deze zal de bestellingen verder doorgeven aan een bediende. Deze hebben wat tijd nodig om een bestelling af te handelen, dus er zijn er meerdere, om meer bestellingen aan te kunnen. Hoe de winkel kiest welke taak aan welke bediende te geven, zouden we op verschillende manieren kunnen oplossen (b.v. “neem een willekeurige vrije bediende”), maar zoveel detail laten we hier niet zien.

Figuur 2: De boekenwinkel aktors. Bij de standaard werking zullen bestellingen van klanten doorgegeven worden aan een pool van bediendes. Wanneer een boek niet kan worden geleverd, krijgt de klant in de plaats ervan een cadeaubon.

De bediende die een bestelling krijgt wordt “bezig” (een andere toestand dan “vrij”) en zal uiteindelijk een boek terugsturen naar de klant (in het geval van het IT systeem dat dit systeem modelleert, kan dit b.v. een bericht zijn met de tracking code van het pakket). Daarna wordt de bediende terug “vrij”.

Soms gaat er echter iets mis met de bestelling: de bediende vindt b.v. het boek niet dat de klant wil. In dat geval stuurt deze een verontschuldigend bericht terug naar de klant, met een cadeaubon. Als er echter te vaak van deze dingen gebeuren, krijgt een bediende teveel stress en geeft deze er de brui aan (zijn actor gaat in error-modus). Dit geeft dus een foutmelding, die geëscaleerd wordt naar de supervisor van de bediende, in dit geval de winkel (zie Fig. 3). Deze zal dan moeten beslissen wat er met de fout gebeurt: we kiezen ervoor om de bestelling aan een andere bediende te geven en de gestresseerde bediende een paar dagen “verlof” te geven (waarna zijn actor terug in toestand “vrij” zal komen).

Figuur 3: Illustratie van de verschillende toestanden waarin een bediende zich kan bevinden, plus: wat gebeurt er indien een bediende er de brui aan geeft tijdens een bestelling (Error)? De bediende krijgt verlof van de winkel en de bestelling wordt doorgegeven aan een andere bediende.

Dit voorbeeld illustreert het principe van resiliëntie, gebruikmakende van actoren: de fout wordt niet speciaal als fout behandeld, maar als een bericht naar de winkel, die het probleem op dat niveau kan oplossen. Er wordt ook niet alleen gezorgd voor fout-tolerantie, maar ook voor veerkracht: uiteindelijk zal de foutieve actor worden hersteld en terug ter beschikking komen van de pool. Deze veerkracht maakt net het verschil tussen gewone fout-tolerantie en echte resiliëntie.

Besluit

De meeste bestaande raamwerken voor Reactive systemen focussen zich op het gebruik van streams van berichten/events, en op hoe deze zo efficiënt mogelijk te behandelen. Een raamwerk zoals Akka doet het net iets anders, door gebruik te maken van het actor paradigma. Dit biedt een verfrissende kijk op multi-threaded programming, en kan het bouwen van sommige systemen net iets gemakkelijker maken.

Het gebruik van actoren verhoogt in ieder geval het niveau van abstractie wanneer men over parallellisme en multi-threading probeert te redeneren, een oefening die berucht is voor zijn moeilijkheid.

Actors lijken erg nuttig voor het modelleren van zaken die een toestand en een gedrag vertonen in de echte wereld, zoals mensen, machines, of bedrijven. In het algemeen zijn ze goed inzetbaar wanneer je systeem goed decomposeerbaar is in een set van onafhankelijke taken, of een set van taken die een bepaalde workflow volgt. Ten slotte zijn ze ook erg geschikt voor het programmeren van Event Driven systemen.

_________________________

Dit is een ingezonden bijdrage van Koen Vanderkimpen, IT consultant bij Smals Research.  Dit artikel werd geschreven in eigen naam en neemt geen standpunt in namens Smals.

Leave a Reply

Your email address will not be published.