Reactive hoe doe je dat dan?

Een reactive applicatie is gebaseerd op drie pijlers.

  • Messaging en events, actors en eventsourcing
  • Functioneel programmeren
  • Domain modelleren

Dit alles gebaseerd op een fundament van BigData.

En dat hangt nauw met elkaar samen.

Events en het Actor model

In een reactive applicatie worden events verwerkt door Actors. Het actor model is een alternatief computing model. Berekeningen worden uitgevoerd door een bericht te sturen naar een actor. De actor heeft een input queue, en handelt de berichten in volgorde een voor een af. De actor heeft state, en na ieder bericht is de state aangepast. De actor kan natuurlijk ook weer berichten naar andere actors sturen. Het staat in contrast met het van Neumann model, waarin imperatief programmeren, middels een processor die stap voor stap instructies uitvoer. Actors zijn bedacht door Carl Hewitt (Actor Model of Computation).

Je kunt het zien als een logische volgende stap van TP-monitor (processen) naar Appserver (threads) naar Actor (Reactive). We krijgen als het ware steeds kleinere eenheden.

Events hebben ook te maken met Event Sourcing. Zie de bliki van Martin Fowler (Event Sourcing). Een bekende grap is dat de database eigenlijk niet meer is dan een optimalisatie om snel de laatste stand van data uit de journals te kunnen lezen. Journals kunnen natuurlijk worden gebruikt om de database te reconstrueren bij een calamiteit. In eventsourcing is het geen grap meer, maar de normale gang van zaken. Van een gegeven slaan we de begintoestand op en de mutatie events. Als iemand zijn saldo wil weten, dan berekenen we dat uit de beginstand en de mutaties. En uiteraard is het goed om dat af en toe bij te werken, zodat de log kort blijft, maar dat is een optimalisatie.

Domain modelleren

Domain Driven Development beschrijft het domein model in termen van de business, het kennis-domein. In een domein model worden verschillende soorten objecten onderscheiden. Value objecten bevatten een value, logisch natuurlijk, en die kun je dus niet muteren. Een waarde bijvoorbeeld drie, blijft altijd drie, en als je er eentje bij krijgt, dan heb je er vier, maar drie is nog altijd drie. Drie en vier zijn dus verschillende values. Het is in het algemeen een object met een alleen een constructor en getters. Entiteiten hebben een lifecycle, en die kunnen tijdens de lifecycle gemuteerd worden. Daarom is er ook een identiteit nodig, om het aan te kunnen duiden. Repositories regelen de administatie en persistentie van de entiteiten. Een Student zou een entiteit kunnen zijn en een adres een value object. Als de student verhuist, dan krijgt hij een ander adres, en iemand ander gaat wellicht op het oude adres wonen.

Een aggregate is een Entiteit op een wat groffer niveau. Het bestaat in het algemeen uit een entiteit, met wat andere entiteiten die daar bijhoren. Een order en order regels en betalingen zou een voorbeeld kunnen zijn.

Er zijn nog veel meer begrippen zie bijvoorbeeld Implementing Domain Driven Design.

Functioneel programmeren

De derde pijler is functioneel programmeren. Functionele programmeertalen doen eigenlijk niks anders dan expressies uitrekenen. een functie krijgt input argumenten mee, en berekent een antwoord als output, en dan is het klaar. Er zijn geen zij-effecten, er wordt niet ondertussen even iets van het netwerk of uit een bestand gehaald, of weggeschreven. De output is een functie van de input. Precies zoals wiskundigen dat graag zien.

Dat klinkt simpel, maar er zit een wereld onder. Functies zijn ook gewoon values, en je hebt functies, waarmee je functies kunt samenstellen (hogere orde functies).  En zo kun je complexe berekeningen op een hoog abstractie niveau uitvoeren. Het is wel even wennen als je imperatieve talen gewend bent.

En dit tezamen

Dit werkt mooi samen. Een functioneel domein model beschrijft het domein op een hoog abstractie niveau, waardoor ontkoppeling en meerdere implementatie vormen beschikbaar zijn. Je moet het domein model zien als een soort abstracte syntax, die op verschillende manieren als concrete syntax kunt weergeven.

Functioneel programmeren helpt niet alleen om het domein model als algebra te beschrijven, het werkt ook mooi in een actor. De actor verwerkt een bericht uit de queue, en update zijn state. Het uitrekenen van de nieuwe state is dus een functie van de oude state en een bericht. Hiervoor is een functionele taal dus prima geschikt.

Er is ook een relatie tussen het domein model en het actor model. We brengen een aggregate root onder in een actor. De oude ACID transacties kunnen we vergeten. De synchronisatie vindt plaats op actor niveau, en op actor niveau is alles vanzelf serializable. En het is makkelijk te controleren en na te rekenen.

Conclusie

Het drietal Domain modelling, Functioneel programmeren, en het Actor model vormen een uitstekende combinatie van technieken om applicaties mee te bouwen.

Door in een actor functies te gebruiken, die een aggregate van de ene consistente toestand in de andere brengen kunnen we een bepaalde integriteit garanderen. Synchronisatie tussen actors is er niet. Als er geld wordt overgemaakt van de ene naar de andere actor dan zijn ze niet op hetzelfde moment consistent. We noemen dat eventual consistency, en dat is de opvolger van het de ACID transacties.

Dat is anders denken. En dat maakt het juist interessant.

Reactive Programming

Reactive programming is een nieuwe architectuur stijl op grote applicaties te ontwikkelen. Er is een Reactive Manifesto verschenen, bekritiseerd vanwege de onduidelijkheid. Tja dat was met het Agile Manifesto ook wel, en kom nog wel eens mensen tegen die er niet zo in geloven. Maar niet zo veel meer…

Het manifesto beoogt systemen te bouwen die flexibel en schaalbaar zijn, waarvan de componenten losse koppelingen kennen. De vier principes zijn:

  • Message driven.
    Dit zorgt vanzelf al voor een ontkoppeling en locatie transparantie van applicaties. Componenten werken asynchroon, eigenlijk ook een vorm van ontkoppeling.
  • Resilient.
    Veerkrachtig, in geval van uitval van onderdelen blijft het systeem reageren. Replicatie van componenten is een belangrijk facet hiervan.
  • Elastic.
    Het systeem reageert elastisch op de workload. Bij hoge belasting kunnen snel meer bronnen worden ingezet, en bij afname van de workload kunnen deze weer worden vijgegeven.
  • Responsive
    Het systeem reageert altijd betrouwbaar en snel.

Deze dingen hangen natuurlijk met elkaar samen. Het systeem kan responsive zijn doordat het elastisch en veerkrachtig is. Deze eigenschappen kunnen worden bereikt doordat snel meer replica’s van componenten kunnen worden ingezet. Deze replica’s kunnen allemaal gelijk worden gehouden door dezelfde events naar alle replica’s te sturen. Logisch allemaal.

Ik heb ruim 30 jaar ervaring, en ik heb van alles gezien: mini-computers, realtime en timesharing systemen op de PDP-11 en VAX, transactiemonitors zoals Tuxedo en ACMS, webservers, (de CERN server en apache) en applicatieservers zoals JBoss en Weblogic, en databases van indexed files, Codasyl en relational database, en tegenwoordig BigData.

Het lijkt mij dat het reactive manifesto een nieuw tijdperk inluidt, met veel nieuwe ontwikkelingen. Het lijkt me een stap grote stap voorwaarts, groter dan de overgang van tpmonitor naar appserver. Ik doe mee, ik ga dit met grote belangstelling volgen, en ik ben benieuwd wat we allemaal gaan leren.

Reactive stacks

Reactive applicatie kunnen worden gebouwd met allerlei technologie stacks.

  • MEAN stack: MongoDB Express Angular en Node.js voor javascript adepten.
  • De Typesafe stack, Scala en Akka.
  • Microsoft F# Rx Linq
  • En Java of Groovy kan ook ingezet worden.

Reactive versus CRUD.

In welk opzicht is Reactive anders dan de applicaties die we gewend zijn? In een andere blog heb al eens een aantal andere stijlen op een rijtje gezet. Nu gaan we dat eens vergelijken.

Reactive versus CRUD

De meeste JEE applicatie muteren gegevens in een database volgens CRUD. Het is soort lifecycle, gegevens worden gecreerd, geraadpleegd, update en delete. Reactive systemen zijn message driven. Een message wordt eenmalig verwerkt. De message kan natuurlijk worden bewaard, maar een update, dat is een nieuwe message.

Asynchroon versus synchroon

Webapplicatie werken request response. Reactive applicatie werken asynchroon.

Eventual consistency versus ACID

Een database kent serializable transacties. Een transactie is atomair, ze kunnen elkaar niet beïnvloeden. Je kunt altijd een volgorde vinden waarin de transacties als ze achter elkaar gezet worden het zelfde resultaat opleveren, als wanneer ze parallel draaien. De database vergrendelt gegevens, als een andere transactie bezig, dan wordt de toegang gesynchroniseerd.
Reactive applicaties sturen berichten, die weer andere berichten triggeren. De ene component gaat zijn gegeven bijwerken, een andere even later. Waardoor tijdelijk gegevens niet met elkaar kloppen. Uiteindelijk komt het allemaal wel weer goed, en dat noemen we dan Eventual consistency. We verliezen niks, maar het duurt even.

Non-deterministisch versus serializable

Reactive systemen zijn hierdoor non-deterministisch. Serializable niet. Er zijn overigens niet zo heel veel databases die dat echt goed kunnen.

Event sourcing versus state update.

Een component in een reactive systeem weet niet altijd wat de status is van een gegeven. Hij moet het uitrekenen uit de beginstand en de binnengekomen berichten. Met Hibernate heb je meteen de actuele versie van een CRUD record te pakken.

Gedistribueerd versus centrale database

Een reactive applicatie kan bestaan uit meerdere autonome micro services. Dit zijn een soort subsystemen die autonoom kunnen worden onderhouden. Dit kan ook wel met database centrische Jee applicaties, maar het is niet zo makkelijk een join te doen over twee databases, en in het algemeen is een meer monolithische architectuur gebruikelijk.

Message driven versus enterprise bean.

Binnen reactive zijn message de centrale aandriving. Bij JEE zijn dat de enterprise beans, die via OR-mapping de persistentie regelen.

Microservice versus monolithisch.

Al eerder genoemd worden reactive applicaties opgedeeld in modulaire subsystemen. Dit maakt zaken als upgrades wel makkelijker. Als de interfaces met andere systemen compatible blijven kan ieder onderdeel een eigen lifecycle en evolutie ondergaan, en kunnen delen vervangen worden. Dit is een voordeel ten opzichte van applicaties die nauwer gekoppeld zijn.

 

Andere stijlen dan Reactive.

In deze blog beschrijf ik een aantal andere architectuur stylen dan reactive die er zoal zijn geweest in de tijd. In een andere blog kunnen we die dan eens vergelijken met Reactive.

Batch

In de tijd dat computers verschrikkelijk duur waren en goed opgesloten in airconditioned ruimtes was batch de dominante stijl van verwerking. Je levert een doos met ponskaarten in bij het rekencentrum. Het rekencentrum verwerkt de input, en de (meestal de volgende dag) kun je een pak papier, de output weer ophalen, en dan blijk dat de compiler een fout in je (COBOL) programma heeft gevonden, de run niet is doorgegaan, en de volgende keer beter.

Dit klinkt niet erg agile, maar batch verwerking heeft wel degelijk voordelen.

  • Een run heeft een gecontroleerde begintoestand en eind toestand. Het is makkelijk te corrigeren (databases waren er nog niet), je leest een input tape en schrijft een output tape.
  • Het is allemaal goed te plannen en te beheerbaar.
  • Throughput is optimaal. (De response tijd niet)

Timesharing

Meerdere gebruikers delen dezelfde machine. Dit was kostenbesparend in de tijd dat computers kostbaar waren, en gebruikers kunnen sneller werken. In deze tijd van Cloud computing is dat allemaal vervallen als voordeel. Een dingetje is nog van belang:

  • Gebruikers kunnen gegevens delen.

Real-time systemen.

Een systeem om een staalwals te besturen bijvoorbeeld. Een plaat staal begint zijn leven als enkele decimeters dik, en vele honderden graden heet. Langzaam rolt het de wals-straat binnen en wordt uitgerold tot minder dan een millimeter dikte. Dan gaat het allemaal loeihard, en dan moet een staalwals binnen enkele milliseconden bijgestuurd kunnen worden om een gelijkmatige dikte te kunnen krijgen.

  • Het reageert op tijd. Er geldt een exacte tijd die altijd gehaald moet worden. Dan is het snel genoeg of anders te traag, en dan werkt het niet.
  • Het is dus niet zo snel mogelijk, nee binnen de gespecificeerde tijd is goed genoeg.
  • En die gespecificeerde tijd kan ook minuten of uren zijn, een chemisch proces is iets anders dan straaljager. Beide hebben realtime besturing nodig.

Transactie monitor.

Grote aantallen gebruikers bedienen gaat niet met een timesharing systeem. Iedere gebruiker heeft een eigen proces en dat schaalt niet. We moeten meer gebruikers met minder processen laten werken. Dus wat nieuws uitgevonden, een transactiemonitor, die kan 1000 gebruikers bedienen met 50 processen.

De gebruiker vult een formulier in, en drukt op “transmit”. De TP monitor plaatst het bericht een queue, en zodra een proces beschikbaar is wordt het bericht verwerkt en een response gegeven. Omdat het gaat om korte transacties, worden de queue’s niet al te lang, en zo kunnen we goede responsietijden halen.

Dit best wel mooie technologie. Het had grote voordelen.

  • Het dwong programmeurs om programma’s als kleine modules te ontwikkelen. Alleen al hierdoor werd de productiviteit zodanig verbeterd, dat COBOL projecten soms beter scoorden dan 4Gl’s.
  • Die 50 server processen kunnen optimaal getuned worden op de achterliggende database structuur. Je doet niet 50 processen die allemaal hetzelfde kunnen doen, nee vijf processen die updates doen en 45 die lezen. Minder kans op database locking of resource contention. Nog subtieler om lees processen te specialiseren naar database gebieden. Zo kun je de hardware naar de limieten pushen.
  • Goede grip op prestaties, beschikbaarheid, failover mogelijkheden. Goed beheerbaar.
  • Sommige TP-monitors hadden messaging faciliteiten, waarmee transacties in persistente queues konden worden gezet, waardoor efficiency en loose coupling kon worden bereikt.

Appservers en Object-oriëntatie

Het OO paradigma leek een veelbelovend concept. De eerste pogingen om tot OO middleware te komen zoals CORBA en J2EE hebben nog niet veel bruikbare middleware opgeleverd. Gedistribueerde objecten bleken kwetsbaar, en niet schaalbaar. J2EE was ook nog gebaseerd op deze concepten, het was bewerkelijk om applicaties te bouwen, en transacties lekten dirty data via beans.

Inkapseling was een belangrijk principe dat voor ogen stond. Tja, als je een database met goede prestaties en goede transactie isolatie gaat inkapselen dan kun je eindigen met een database die slecht presteert en gegevens corrumpeert. De praktijk is dat ik vaker heb gemigreerd naar een andere appserver, dan naar een andere database.

Maar goed, inmiddels zijn we door deze kinderziektes heen. Dankzij opensource Spring en Hibernate is er een werkbare standaard ontstaan. En met een moderne appserver kun je goede applicaties bouwen. De eigenschappen zijn:

  • Standaards. Http, Webservices, REST, Xml, Json, Html JEE, het werkt allemaal behoorlijk op dezelfde manier. Daardoor is makkelijk kennis, componenten en frameworks te hergebruiken.
  • Productiviteit. Het is niet zo heel veel minder werk om een functie te bouwen op een appserver als op een TP-monitor, maar het functioneert allemaal in veel complexere wereld, met ajax, rich clients, apps, tablets, devices, e-commerce en Ldap servers. Dat is wel iets anders dan een character-cell terminal met forms pakket.
  • Beheerbaarheid. Ook appservers hebben faciliteiten voor failover loadbalancing, clustering, waardoor hoge service niveau’s kunnen worden gehaald.
  • Threading en garbage collection. De processen van de TP-monitor zijn vervangen door threads binnen een proces. Dat is efficiënter, maar als programmeur moet je er wel rekening mee houden.

 

 

 

 

 

 

 

Wat betekent Reactive system?

Ja, wat is eigenlijk een reactive system?

Je kunt het definiëren als een systeem dat asynchroon input ontvangt langs verschillende kanalen en daar op reageert met output. De output kan langs allerlei andere kanalen gebeuren.

reactive_system

Zoiets eigenlijk.

We kennen voorbeelden genoeg. Twitter kun je zien als een reactive system. Maar ook bijvoorbeeld een lift besturing, ontvangt input van de etages, van het controle paneel in de lift zelf, de sensors in de lift, en geeft als output commando’s naar de lift.