5 Dingen om te Weten Over Reactief Programmeren

Door Clement Escoffier 30 juni 2017Augustus 6 augustus 2020

Reactief, wat een overbeladen woord. Veel dingen blijken tegenwoordig magisch Reactief te worden. In deze post gaan we het hebben over Reactive Programming, d.w.z. een ontwikkelingsmodel dat is gestructureerd rond asynchrone gegevensstromen.

Ik weet dat je ongeduldig bent om je eerste reactieve applicatie te schrijven, maar voordat je het doet, zijn er een paar dingen die je moet weten. Het gebruik van reactief programmeren verandert de manier waarop u uw code ontwerpt en schrijft. Voordat je op de trein springt, is het goed om te weten waar je naartoe gaat.

In deze post gaan we 5 dingen uitleggen over reactief programmeren om te zien wat het voor jou verandert.

Wanneer je reactief programmeren gebruikt, gaan gegevensstromen de ruggengraat van je applicatie vormen. Gebeurtenissen, berichten, oproepen, en zelfs storingen worden overgebracht door een datastroom. Met reactief programmeren observeer je deze stromen en reageer je wanneer een waarde wordt uitgezonden.

In je code ga je dus datastromen maken van alles en nog wat: klikgebeurtenissen, HTTP-verzoeken, ingested berichten, beschikbaarheidsmeldingen, wijzigingen in een variabele, cachegebeurtenissen, metingen van een sensor, letterlijk alles wat er kan veranderen of gebeuren. Dit heeft een interessant neveneffect op uw toepassing: deze wordt inherent asynchroon.

Reactive eXtension (http://reactivex.io, a.ka. RX) is een implementatie van de reactieve programmeerprincipes om “asynchrone en event-gebaseerde programma’s samen te stellen door gebruik te maken van observeerbare sequentie”. Met RX creëert uw code en abonneert u zich op gegevensstromen die Observables worden genoemd. Terwijl Reactive Programming over de concepten gaat, biedt RX je een verbazingwekkende gereedschapskist. Door de observer- en iteratorpatronen en functionele idiomen te combineren, geeft RX je superkrachten. Je hebt een arsenaal aan functies om te combineren, samen te voegen, te filteren, te transformeren en de datastromen te creëren. Het volgende plaatje illustreert het gebruik van RX in Java (met https://github.com/ReactiveX/RxJava).

Hoewel RX niet de enige implementatie is van de reactieve programmeerprincipes (we kunnen bijvoorbeeld BaconJS aanhalen – http://baconjs.github.io), is het vandaag de dag wel de meest gebruikte. In de rest van deze post, gaan we Rx Java.

2. Observables kan koud of warm – en het maakt uit.

Op dit punt, bent u proberen om te zien wat zijn de verschillende streams (of observables) je gaat om te gaan met in uw programma. Maar er zijn twee klassen van stromen: warme en koude. Het verschil begrijpen is de sleutel tot succesvol gebruik van reactief programmeren.

Koude observabelen zijn lui. Ze doen niets totdat iemand ze begint te observeren (inschrijven in RX). Ze beginnen pas te lopen als ze worden geconsumeerd. Cold streams worden gebruikt om asynchrone acties weer te geven, bijvoorbeeld, dat het niet wordt uitgevoerd totdat iemand geïnteresseerd is in het resultaat. Een ander voorbeeld zou een bestandsdownload zijn. Het zal niet beginnen de bytes te trekken als niemand iets met de gegevens gaat doen. De gegevens van een koude stroom worden niet gedeeld tussen abonnees en als je je abonneert, krijg je alle items.

Hot streams zijn actief voor het abonnement, zoals een beurstikker, of gegevens die worden verzonden door een sensor of een gebruiker. De gegevens zijn onafhankelijk van een individuele abonnee. Wanneer een waarnemer zich abonneert op een hot observable, krijgt hij alle waarden in de stroom die worden uitgezonden nadat hij zich heeft geabonneerd. De waarden worden gedeeld door alle abonnees. Bijvoorbeeld, zelfs als niemand zich heeft geabonneerd op een thermometer, meet en publiceert deze de huidige temperatuur. Wanneer een abonnee zich op de stream aanmeldt, ontvangt deze automatisch de volgende meting.

Waarom is het zo belangrijk om te begrijpen of je streams warm of koud zijn? Omdat het verandert hoe uw code de overgebrachte items consumeert. Als u niet bent geabonneerd op een hot observable, ontvangt u de gegevens niet en gaan deze verloren.

Ontwikkel met behulp van de meest waardevolle producten van Red Hat

Uw lidmaatschap geeft toegang tot Red Hat-producten en technische training over de ontwikkeling van enterprise cloudapplicaties.

JOIN RED HAT DEVELOPER

Misbruikte asynchronie bijt

Er is één belangrijk woord in de definitie van reactief programmeren: asynchroon. Je krijgt een melding als er gegevens in de stroom worden uitgezonden, asynchroon – dat wil zeggen onafhankelijk van de hoofdstroom van het programma. Door uw programma te structureren rond gegevensstromen, schrijft u asynchrone code: u schrijft code die wordt aangeroepen wanneer de stroom een nieuw item uitzendt. Threads, blokkeringscode en neveneffecten zijn zeer belangrijke zaken in deze context. Laten we beginnen met neveneffecten.

Functies zonder neveneffecten interageren met de rest van het programma uitsluitend via hun argumenten en return-waarden. Neveneffecten kunnen heel nuttig zijn en zijn in veel gevallen onvermijdelijk. Maar ze hebben ook valkuilen. Wanneer je reactief programmeren gebruikt, moet je onnodige neveneffecten vermijden, en een duidelijke bedoeling hebben wanneer je ze toch gebruikt. Dus, omarm immutabiliteit, en neveneffect vrije functies. Hoewel sommige gevallen gerechtvaardigd zijn, leidt misbruik van side-effects tot onweer: thread safety.

Dat is het tweede belangrijke punt: threads. Het is leuk om streams te observeren en een melding te krijgen als er iets interessants gebeurt, maar je moet nooit vergeten wie je aanroept, of preciezer gezegd op welke thread je functies worden uitgevoerd. Het is sterk aan te raden om niet te veel threads in je programma te gebruiken. Asynchrone programma’s die op meerdere threads vertrouwen worden een lastige synchronisatie puzzel die vaak eindigt als een deadlock hunt.

Dat is het derde punt: blokkeer nooit. Omdat je niet de eigenaar bent van de thread die je aanroept, moet je er zeker van zijn dat je die nooit blokkeert. Als je dat wel doet, kun je voorkomen dat de andere items worden uitgezonden, ze zullen worden gebufferd totdat … de buffer vol is (back-pressure kan in dit geval in werking treden, maar dat is niet het onderwerp van deze post). Door RX en asynchrone IO te combineren heb je alles wat je nodig hebt om non-blocking code te schrijven, en als je meer wilt, kijk dan eens naar Eclipse Vert.x, een reactieve toolkit die reactiviteit en asynchronie bevordert. Bijvoorbeeld, de volgende code toont de Vert.x Web Client en zijn RX API om een JSON document van de server op te halen en de naam te tonen:

client.get("/api/people/4").rxSend().map(HttpResponse::bodyAsJsonObject).map(json -> json.getString("name")).subscribe(System.out::println, Throwable::printStackTrace);

Merk de subscribe methode in dit laatste fragment op. Het neemt een tweede methode die wordt aangeroepen wanneer een van de verwerkingsstadia een uitzondering gooit. Vang altijd de uitzonderingen. Als je dat niet doet, ben je uren bezig om te begrijpen wat er fout gaat.

4. Keep things simple

Zoals je weet: “Met grote macht komt grote verantwoordelijkheid.” RX biedt veel coole functies, en het is makkelijk om naar de duistere kant te neigen. Het aaneenschakelen van flapmap, retry, debounce en zip geeft je het gevoel een ninja te zijn… MAAR, vergeet nooit dat goede code leesbaar moet zijn voor iemand anders.

Laten we eens wat code nemen…

manager.getCampaignById(id) .flatMap(campaign -> manager.getCartsForCampaign(campaign) .flatMap(list -> { Single<List<Product>> products = manager.getProducts(campaign); Single<List<UserCommand>> carts = manager.getCarts(campaign); return products.zipWith(carts, (p, c) -> new CampaignModel(campaign, p, c)); }) .flatMap(model -> template .rxRender(rc, "templates/fruits/campaign.thl.html") .map(Buffer::toString)) ) .subscribe( content -> rc.response().end(content), err -> { log.error("Unable to render campaign view", err); getAllCampaigns(rc); });

Een voorbeeld als dit is soms moeilijk te begrijpen, niet? Het ketent verschillende asynchrone operaties (flatmap), samenvoegen van een andere set van operaties (zip). Reactief programmeren code vereist eerst een mind-shift. Je wordt op de hoogte gebracht van asynchrone gebeurtenissen. Vervolgens kan de API moeilijk te begrijpen zijn (kijk maar naar de lijst van operatoren). Maak geen misbruik, schrijf commentaar, leg uit, of teken diagrammen (ik weet zeker dat je een asciiart artiest bent). RX is krachtig, er misbruik van maken of het niet uitleggen zal je collega’s chagrijnig maken.

5. Reactief programmeren != Reactief systeem

Ongetwijfeld het meest verwarrende deel. Met reactief programmeren bouw je geen reactief systeem. Reactieve systemen, zoals gedefinieerd in het reactive manifesto, zijn een architectuurstijl om responsieve gedistribueerde systemen te bouwen. Reactieve systemen kunnen worden gezien als gedistribueerde systemen op de juiste manier. Een reactief systeem wordt gekenmerkt door vier eigenschappen:

  • Responsive: een reactief systeem moet verzoeken binnen een redelijke tijd afhandelen (ik laat u zelf bepalen wat redelijk is).
  • Resilient: een reactief systeem moet responsief blijven bij storingen (crash, timeout, 500 errors… ), dus het moet ontworpen zijn voor storingen en er op de juiste manier mee omgaan.
  • Elastic: een reactief systeem moet responsief blijven bij verschillende belastingen. Bijgevolg moet het op en neer schalen, en in staat zijn om de belasting met minimale middelen aan te kunnen.
  • Berichtgedreven: componenten van een reactief systeem interageren met behulp van asynchrone berichtenoverdracht.

Ondanks de eenvoud van deze fundamentele principes van reactieve systemen, is het bouwen van een ervan lastig. Typisch moet elk knooppunt een asynchroon non-blocking ontwikkelingsmodel omarmen, een taak-gebaseerd concurrency model en gebruikt non-blocking I/O. Als je niet eerst over deze punten nadenkt, wordt het al snel een spaghettiplaat.

Reactief programmeren en Reactive eXtension bieden een ontwikkelmodel om het asynchrone beest te temmen. Door het verstandig te gebruiken, zal uw code leesbaar en begrijpelijk blijven. Het gebruik van reactief programmeren verandert uw systeem echter niet in een Reactief Systeem. Reactieve Systemen zijn het volgende niveau.

Conclusie

We bereiken eindelijk het einde van deze post. Als je verder wilt gaan en geïnteresseerd bent in reactive, raad ik je aan eens te kijken naar Eclipse Vert.x – een toolkit om reactive en gedistribueerde systemen te bouwen (http://vertx.io), en naar het Reactive Microservices in Java minibook dat verkrijgbaar is bij https://developers.redhat.com/books/building-reactive-microservices-java. De combinatie van Vert.x en Reactive eXtension ontketent uw reactieve superkracht. U kunt niet alleen reactief programmeren, maar ook reactieve systemen bouwen en toegang krijgen tot een spannend en groeiend ecosysteem.

Happy coding!

Download het Eclipse Vert.x spiekbriefje, dit spiekbriefje geeft stap voor stap details waarmee u uw apps kunt maken zoals u dat wilt.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.