5 cose da sapere sulla programmazione reattiva

Di Clement Escoffier 30 giugno 2017-6 agosto 2020

Reactive, che parola sovraccarica. Molte cose risultano diventare magicamente reattive in questi giorni. In questo post, parleremo della Programmazione Reattiva, cioè un modello di sviluppo strutturato intorno a flussi di dati asincroni.

So che siete impazienti di scrivere la vostra prima applicazione reattiva, ma prima di farlo, ci sono un paio di cose da sapere. L’uso della programmazione reattiva cambia il modo in cui progettate e scrivete il vostro codice. Prima di saltare sul treno, è bene sapere dove si sta andando.

In questo post, spiegheremo 5 cose sulla programmazione reattiva per vedere cosa cambia per voi.

Quando si usa la programmazione reattiva, i flussi di dati saranno la spina dorsale della vostra applicazione. Eventi, messaggi, chiamate e persino fallimenti saranno trasmessi da un flusso di dati. Con la programmazione reattiva, si osservano questi flussi e si reagisce quando viene emesso un valore.

Così, nel vostro codice, creerete flussi di dati di qualsiasi cosa e da qualsiasi cosa: eventi click, richieste HTTP, messaggi ingeriti, notifiche di disponibilità, cambiamenti su una variabile, eventi cache, misure da un sensore, letteralmente qualsiasi cosa che possa cambiare o accadere. Questo ha un interessante effetto collaterale sulla vostra applicazione: sta diventando intrinsecamente asincrona.

Reactive eXtension (http://reactivex.io, a.ka. RX) è un’implementazione dei principi della programmazione reattiva per “comporre programmi asincroni e basati su eventi usando una sequenza osservabile”. Con RX, il vostro codice crea e sottoscrive flussi di dati chiamati Observables. Mentre la programmazione reattiva riguarda i concetti, RX fornisce un’incredibile cassetta degli attrezzi. Combinando i modelli osservatore e iteratore e gli idiomi funzionali, RX vi dà superpoteri. Avete un arsenale di funzioni per combinare, unire, filtrare, trasformare e creare i flussi di dati. La prossima immagine illustra l’uso di RX in Java (usando https://github.com/ReactiveX/RxJava).

Anche se RX non è l’unica implementazione dei principi di programmazione reattiva (per esempio possiamo citare BaconJS – http://baconjs.github.io), è la più usata oggi. Nel resto di questo post, useremo Rx Java.

2. Gli osservabili possono essere freddi o caldi – ed è importante.

A questo punto, state cercando di capire quali sono i diversi flussi (o osservabili) con cui avrete a che fare nel vostro programma. Ma ci sono due classi di flussi: caldo e freddo. Capire la differenza è la chiave per usare con successo la programmazione reattiva.

Gli osservabili freddi sono pigri. Non fanno nulla finché qualcuno non inizia ad osservarli (subscribe in RX). Iniziano a funzionare solo quando vengono consumati. I flussi freddi sono usati per rappresentare azioni asincrone, per esempio, che non saranno eseguite finché qualcuno non sarà interessato al risultato. Un altro esempio potrebbe essere il download di un file. Non inizierà ad estrarre i byte se nessuno ha intenzione di fare qualcosa con i dati. I dati prodotti da un flusso freddo non sono condivisi tra gli abbonati e quando ti abboni ricevi tutti gli elementi.

I flussi caldi sono attivi prima dell’abbonamento come un ticker di borsa, o i dati inviati da un sensore o un utente. I dati sono indipendenti da un singolo abbonato. Quando un osservatore si abbona a un osservabile caldo, otterrà tutti i valori nel flusso che sono emessi dopo la sua sottoscrizione. I valori sono condivisi tra tutti i sottoscrittori. Per esempio, anche se nessuno ha sottoscritto un termometro, esso misura e pubblica la temperatura attuale. Quando un sottoscrittore si iscrive al flusso, riceve automaticamente la misura successiva.

Perché è così importante capire se i tuoi flussi sono caldi o freddi? Perché cambia il modo in cui il vostro codice consuma gli elementi trasmessi. Se non sei iscritto a un osservabile caldo, non riceverai i dati, e questi dati sono persi.

Sviluppa utilizzando i prodotti più preziosi di Red Hat

La tua iscrizione sblocca i prodotti Red Hat e la formazione tecnica sullo sviluppo di applicazioni cloud aziendali.

JOIN RED HAT DEVELOPER

Morso asincrono abusato

C’è una parola importante nella definizione di programmazione reattiva: asincrono. Si viene avvisati quando i dati vengono emessi nel flusso in modo asincrono – cioè indipendentemente dal flusso principale del programma. Strutturando il vostro programma intorno ai flussi di dati, state scrivendo codice asincrono: scrivete codice invocato quando il flusso emette un nuovo elemento. Threads, codice bloccante e effetti collaterali sono questioni molto importanti in questo contesto. Cominciamo con gli effetti collaterali.

Le funzioni senza effetti collaterali interagiscono con il resto del programma esclusivamente attraverso i loro argomenti e valori di ritorno. Gli effetti collaterali possono essere molto utili e sono inevitabili in molti casi. Ma hanno anche delle insidie. Quando si usa la programmazione reattiva, si dovrebbero evitare gli effetti collaterali non necessari, e avere una chiara intenzione quando li si usa. Quindi, abbracciate l’immutabilità e le funzioni senza effetti collaterali. Mentre alcuni casi sono giustificati, abusare degli effetti collaterali porta ai temporali: la sicurezza dei thread.

Questo è il secondo punto importante: i thread. È bello osservare i flussi ed essere avvisati quando succede qualcosa di interessante, ma non dovete mai dimenticare chi vi sta chiamando, o più precisamente su quale thread vengono eseguite le vostre funzioni. È fortemente raccomandato di evitare di usare troppi thread nel vostro programma. I programmi asincroni che si basano su più thread diventano un difficile puzzle di sincronizzazione che spesso finisce come una caccia al deadlock.

Questo è il terzo punto: mai bloccare. Poiché non possedete il thread che vi chiama, dovete essere sicuri di non bloccarlo mai. Se lo fate, potreste evitare gli altri elementi da emettere, essi saranno bufferizzati fino a quando … il buffer è pieno (la back-pressure può entrare in gioco in questo caso, ma questo non è l’argomento di questo post). Combinando RX e IO asincrono avete tutto il necessario per scrivere codice non bloccante, e se volete di più, guardate Eclipse Vert.x, un toolkit reattivo che promuove reattività e asincronia. Per esempio, il seguente codice mostra il Web Client Vert.x e la sua API RX per recuperare un documento JSON dal server e visualizzare la voce del nome:

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

Nota il metodo subscribe in questo ultimo frammento. Prende un secondo metodo chiamato quando una delle fasi di elaborazione lancia un’eccezione. Catturate sempre le eccezioni. Se non lo fate passerete ore a cercare di capire cosa sta andando storto.

4. Mantenere le cose semplici

Come sapete, “Da un grande potere derivano grandi responsabilità”. RX fornisce un sacco di funzioni molto belle, ed è facile tendere verso il lato oscuro. Incatenare flapmap, retry, debounce, e zip ti fa sentire come un ninja… MA, non dimenticare mai che il buon codice deve essere leggibile da qualcun altro.

Prendiamo un po’ di codice…

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); });

Dato un esempio come questo può essere difficile da capire no? Si concatenano diverse operazioni asincrone (flatmap), si uniscono ad un altro insieme di operazioni (zip). Il codice di programmazione reattiva richiede innanzitutto uno spostamento mentale. Si viene avvisati di eventi asincroni. Poi, l’API può essere difficile da afferrare (basta guardare l’elenco degli operatori). Non abusate, scrivete commenti, spiegate o disegnate diagrammi (sono sicuro che siete un artista di asciiart). RX è potente, abusarne o non spiegarlo renderà i vostri colleghi scontrosi.

5. Programmazione reattiva != Sistema reattivo

Probabilmente la parte più confusa. Usare la programmazione reattiva non costruisce un sistema reattivo. I sistemi reattivi, come definiti nel manifesto reattivo, sono uno stile architettonico per costruire sistemi distribuiti reattivi. I sistemi reattivi potrebbero essere visti come sistemi distribuiti fatti bene. Un sistema reattivo è caratterizzato da quattro proprietà:

  • Reattivo: un sistema reattivo deve gestire le richieste in un tempo ragionevole (lascio a voi la definizione di ragionevole).
  • Resiliente: un sistema reattivo deve rimanere reattivo di fronte ai fallimenti (crash, timeout, 500 errori… ), quindi deve essere progettato per i fallimenti e affrontarli in modo appropriato.
  • Elastico: un sistema reattivo deve rimanere reattivo sotto vari carichi. Di conseguenza, deve scalare su e giù, ed essere in grado di gestire il carico con risorse minime.
  • Message driven: i componenti di un sistema reattivo interagiscono usando il passaggio di messaggi asincroni.

Nonostante la semplicità di questi principi fondamentali dei sistemi reattivi, costruirne uno è difficile. Tipicamente, ogni nodo ha bisogno di abbracciare un modello di sviluppo asincrono non bloccante, un modello di concorrenza basato sui compiti e utilizza I/O non bloccanti. Se non si pensa prima a questi punti, diventerà rapidamente un piatto di spaghetti.

La programmazione reattiva e Reactive eXtension fornisce un modello di sviluppo per domare la bestia asincrona. Usandolo saggiamente, il vostro codice rimarrà leggibile e comprensibile. Tuttavia, usare la programmazione reattiva non trasforma il vostro sistema in un sistema reattivo. I sistemi reattivi sono il livello successivo.

Conclusione

Siamo finalmente arrivati alla fine di questo post. Se volete andare oltre e siete interessati al reattivo, vi consiglio di dare un’occhiata a Eclipse Vert.x – un toolkit per costruire sistemi reattivi e distribuiti (http://vertx.io), e al minibook Reactive Microservices in Java disponibile da https://developers.redhat.com/books/building-reactive-microservices-java. La combinazione di Vert.x e Reactive eXtension scatena il vostro superpotere reattivo. Puoi non solo usare la programmazione reattiva ma anche costruire sistemi reattivi e avere accesso a un ecosistema entusiasmante e in crescita.

Felice codifica!

Scarica il cheat sheet di Eclipse Vert.x, questo cheat sheet fornisce dettagli passo dopo passo per permetterti di creare le tue app come vuoi tu.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.