A very introductive presentation about reactive programming principles. Starting from synchronous standard programming, I try to present the needs that driven to reactive programming. Callbacks, Future, Promises, Observables and Actor model are the main issues. I also make an introduction to the Reactive Manifesto.
The presentation is took from the Software Engineering course I run in the bachelor-level informatics curriculum at the University of Padova.
1. PRINCIPI DI
REACTIVE PROGRAMMING
INGEGNERIA DEL SOFTWARE
Università degli Studi di Padova
Dipartimento di Matematica
Corso di Laurea in Informatica, A.A. 2013 – 2014
rcardin@math.unipd.it
2. Ingegneria del software mod. B
GO REACTIVE!
Applicazione anni 2000
Decine di server
Tempi di risposta nell’ordine dei secondi
Ore di downtime per manutenzione
Dati nell’ordine dei Gigabyte
Accedute da dispositivi desktop
Applicazione moderne (≥ 2010)
Cluster di migliaia di processi multicore
Tempi di risposta nell’ordine dei millisec
100% uptime
Dati nell’ordine dei Petabyte
Accedute da qualsiasi tipo di dispositivo
2
Riccardo Cardin
3. Ingegneria del software mod. B
GO REACTIVE!
Nuovi requisiti richiedono nuove tecnologie
Reactive Application
Orientate agli eventi
Scalabili
Resilienti
Responsive
3
Riccardo Cardin
"Readily responsive to a stimulus"
Merriam-Webster
react to events
La natura event-driven abilita
alle altre qualità
react to load
La scalabilità non deve dipendere
da risorse condivise
react to failure
Sistemi resilienti permettono
di recuperare errori a tutti
i livelli
react to users
I tempi di risposta non devono
dipendere dal carico di lavoro
4. Ingegneria del software mod. B
REACTIVE MANIFESTO
4
Riccardo Cardin
responsive
scalable resilient
event-driven
• Loosely coupled design
• Communication orientation
• Uso efficiente delle risorse
• Nessun redesing per
ottenere la scalabilità
• Scalabilità on-demand
• Risk-management
• Downtime è perdita
di denaro
• Parte del design
• Real-time, engaging, reach,
collaborative
• Nessuna latenza nelle
risposte
5. Ingegneria del software mod. B
MODELLO SINCRONO
Il mondo sincrono è senza futuro...
Frequenti operazioni di I/O
Scarso utilizzo delle feature offerte dai processori moderni
Larga diffusione delle architetture multiprocessore
Cicli non ottimizzati
Scarso parallelismo o gestione difficoltosa
Tipico dei linguaggi imperativi e ad oggetti
C, C++, Java, ...
5
Riccardo Cardin
// Synchronous world (w/o Future)
val session = socialNetwork.createSessionFor("user", credentials)
// blocked on method, waiting for results (latency)...
session.getFriends()
// There are a lot of operation to do!!! :(
6. Ingegneria del software mod. B
MODELLO SINCRONO
Il mondo sincrono è senza futuro...
Esempio: recuperare il nome del file più pesante
6
Riccardo Cardin
public String getBiggestFile(final File folder) {
long maxLength = 0L;
String fileName = null;
for (final File fileEntry : folder.listFiles()) {
if (maxLength < fileEntry.length()) { // Not so efficient
fileName = fileEntry.getName();
maxLength = fileEntry.length();
}
}
return fileName ;
}
...
if (maxLength… // computationfileEntry.length() // I/O
CPU poco utilizzata,
I/O sovrabbondante
Java
Tempo totale di attesa
7. Ingegneria del software mod. B
MODELLO ASINCRONO
Callbacks
Funzioni da invocare al termine di una elaborazione
Gestione migliore della CPU
Ridotti tempi di attesa
Più processi gestiscono più richieste
Asynchronous JavaScript and XML (AJAX)
7
Riccardo Cardin
var callback = function(){
alert('I was called back asynchronously');
};
$.ajax({
type: 'GET',
url: 'http://example.com',
done: callback, // positive case
fail: anotherCallback // negative case
});
jQuery
In attesa della risposta, è
possibile continuare
l’elaborazione (rendering UI)
8. Ingegneria del software mod. B
MODELLO ASINCRONO
Callbacks
Linguaggi o framework che gestiscano l’elaborazione
asincrona e concorrente
Node.js, jQuery, ...
Ma...c’è sempre un ma... 8
Riccardo Cardin
CPU1
CPU2
CPU3
CPU4
Tempo totale di attesa
I/O è ancora presente, ma
viene distribuito
sull’elaborazione di più CPU
10. Ingegneria del software mod. B
10
Riccardo Cardin
module.exports = function (dir, cb) {
fs.readdir(dir, function (er, files) {
if (er) return cb(er)
var counter = files.length
var errored = false
var stats = []
files.forEach(function (file, index) {
fs.stat(path.join(dir,file), function (er, stat) {
if (errored) return
if (er) {
errored = true
return cb(er)
}
stats[index] = stat
if (--counter == 0) {
var largest = stats
.filter(function (stat) { return stat.isFile() })
.reduce(function (prev, next) { // [6]
if (prev.size > next.size) return prev
return next
})
cb(null, files[stats.indexOf(largest)])
}
}) // [1]
}) // [2]
}) // [3]
}
node.js
Innesto callback per gestire i
flussi degli eventi
Difficile da verificare e
manutenere
Difficile gestire le
eccezioni con blocchi
try/catch
11. Ingegneria del software mod. B
FUTURES E PROMISES
Futures
Tecnica per eseguire molte operazioni in parallelo, in
modo efficiente e non-blocking
Stile dichiarativo
Immutabile
Rappresenta un segnaposto per un risultato futuro
Componibili
Implementano meccanismi per la gestione delle eccezioni
11
Riccardo Cardin
// With Futures (asynchronous world)
val session = socialNetwork.createSessionFor("user", credentials)
// Create placeholder for computation
val f: Future[List[Friend]] = future { session.getFriends() }
// Statements here can be executed, while waiting for results :)
scala
12. Ingegneria del software mod. B
FUTURES E PROMISES
Monad
Rappresenta un contesto di esecuzione (framework)
Amplificatore di tipi, computation builder
Può racchiudere un tipo
È componibile con altre monadi
Composizione (a.k.a. concatenazione) sul tipo racchiuso
Spesso permette di estrarre il valore del tipo contenuto
Caratteristica dei linguaggi funzionali
12
Riccardo Cardin
public class Monad<T> {
public Monad(T t) { /* ... */ }
}
public class Monad<T> {
public abstract <V> Monad<V> bind(Function<T, Monad<V>> f);
}
13. Ingegneria del software mod. B
FUTURES E PROMISES
Monad
È simile ad una bolla!
13
Riccardo Cardin
Può racciudere
qualcosa
È qualcosa di non
concreto
Può ricevere istruzioni su
cosa fare del contenuto
Può
evaporare,
lasciando
libero il
contenuto
14. Ingegneria del software mod. B
FUTURES E PROMISES
Future[T]è una monad
Racchiudono l’elaborazione da eseguire in modo
asincrono (callback)
Successfully completed / Failed with an exception
Eventual execution: nessun vincolo temporale
14
Riccardo Cardin
import scala.util.{Success, Failure}
// Asynchronous execution inside future context
val f: Future[List[String]] = future {
session.getRecentPosts
}
// Do something else...
f onComplete {
// onSuccess
case Success(posts) => for (post <- posts) println(post)
// onFailure
case Failure(t) => println("An error has occured: " +
t.getMessage)
}
posts è il risulato
dell’elaborazione
t è un’eccezione scala
15. Ingegneria del software mod. B
FUTURES E PROMISES
Future[T]è una monad
Composizione funzionale di più elaborazioni
Non più callback hell!!!
Si riporta il modello asincrono al modello sincrono
Se un future nella catena fallisce, fallisce l’intera catena
15
Riccardo Cardin
// First future execution
val rateQuote = future {
connection.getCurrentValue(USD)
}
// Bind second execution to first
val purchase = rateQuote map { quote =>
if (isProfitable(quote)) connection.buy(amount, quote)
else throw new Exception("not profitable")
}
// Get final result
purchase onSuccess {
case _ => println("Purchased " + amount + " USD")
}
scala
16. Ingegneria del software mod. B
FUTURES E PROMISES
Promise[T]
Single-assigment container (monad) / proxy
Un Future legge il risultato di un’elaborazione asincrona
Una Promise scrive (completa) un Future
16
Riccardo Cardin
val p = promise[T]
// Future that is completed by the promise
val f = p.future
val consumer = future {
startDoingSomething()
f onSuccess {
case r => doSomethingWithResult()
}
}
val producer = future {
val r = produceSomething()
p success r // Promise completes the Future
// It should be „p failure exception‟ also
continueDoingSomethingUnrelated()
}
produceSomething
startDoingSomething
doSomethingWithResult
r
scala
Valore che rappresenta
l’elaborazione
17. Ingegneria del software mod. B
FUTURES E PROMISES
Javascript promises (Q library)
Promise e future sono fusi nelle medesime strutture
Promise stile Scala Q Deferreds
17
Riccardo Cardin
promiseMeSomething()
// value is the return value of promiseMeSomething
.then(function (value) {
// Do something as soon as promiseMeSomething finishes
},
// Something went wrong, reason is an exception
function (reason) {
});
then ritona una
promise, quindi è
concatenabile
var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", function (error, text) {
if (error) { deferred.reject(new Error(error)); }
else { deferred.resolve(text); }
});
return deferred.promise; Deferred permette di
completare una promise.
concatenabile
18. Ingegneria del software mod. B
FUTURES E PROMISES
Java 8 promises: CompletableFuture<T>
Creazione (usando le lambda extension)
Composizione (future stile Scala)
18
Riccardo Cardin
A Future that may be explicitly completed (setting its value and
status), and may include dependent functions and actions that
trigger upon its completion.
Javadoc
final CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> {
//...long running...
return "42";
}, executor);
Effettua l’override del
metodo
CompletableFuture.get()
// f1 is a promise that will completed with an Integer value
CompletableFuture<Double> f3 =
f1.thenApply(Integer::parseInt).thenApply(r -> r * r * Math.PI).
exceptionally(ex -> "We have a problem: " + ex.getMessage());
thenApply è simile a map di
Scala
19. Ingegneria del software mod. B
REACTIVE EXTENSIONS (RX)
Gestione sequenze di valori in modo asincrono
Observable sequences
Estensione del design pattern Observer
Utilizzo di operatori di composizione (monads!)
19
Riccardo Cardin
Single items Multiple items
Synchronous T getData() Iterable<T> getData()
Asynchronous Future<T> getData() Observable<T> getData
public static void hello(String... names) {
Observable.from(names).subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.println("Hello " + s + "!");
}
});
}
// hello("Ben", "George"); Hello Ben! Hello George!
20. Ingegneria del software mod. B
REACTIVE EXTENSIONS (RX)
Observables (RxJava)
20
Riccardo Cardin
Event Iterable (pull) Observable (push)
Retrieve data T next() onNext(T)
Discover error throws Exception onError(Exception)
Complete returns onCompleted()
Aggiunta al
pattern GoF
getVideos().subscribe(new Observer<Video>() {
def void onNext(Video video) { // Invoked on next element
println(“Video: ” + video.videoId)
}
def void onError(Exception e) { // Invoked on error
e.printStackTrace()
}
def void onCompleted() { // Invoked when stream finishes to emit
println(“Completed”)
}
}
21. Ingegneria del software mod. B
REACTIVE EXTENSIONS (RX)
Observables (RxJava)
Monads Componibili
Mantengono la proprietà di asincronia originaria
21
Riccardo Cardin
22. Ingegneria del software mod. B
REACTIVE EXTENSIONS (RX)
Composizione Observables
22
Riccardo Cardin
23. Ingegneria del software mod. B
ACTOR MODEL
Reactive
Elaborazioni solo in risposta a stimoli esterni
Dormiente / attivo
Non c’è un concetto esplicito di thread
Tre azioni fondamentali
Invio di un messaggio async a se stesso o altro attore
Creazione nuovi attori
Modifica del proprio comportamento
Nessuno stato interno (...in teoria...) Immutabili
23
Riccardo Cardin
Each actor is a form of reactive object, executing some
computation in response to a message and sending out a reply
when the computation is done
John C. Mitchell
24. Ingegneria del software mod. B
ACTOR MODEL
Messaggi (task)
Interfaccia di comunicazione dell’attore
Variare nel tempo (comportamento)
Elaborazione dei messaggi uno per volta
Coda di messaggi (mail box)
Nessuna garanzia sull’ordine di arrivo
Mail system
Ogni attore ha associato un mail address
Composti di tre parti
24
Riccardo Cardin
tag target data
email address del
receiver
operazione
25. Ingegneria del software mod. B
ACTOR MODEL
Esempio
Ad ogni cambio di stato viene creato un nuovo attore
State change: no race conditions
Behavioural change
25
Riccardo Cardin
A1:[1, 4, 7]
A1:[1, 2, 4, 7]
A1:[2, 4, 7]
Insert|2
Get_min
Min|1
26. Ingegneria del software mod. B
ACTOR MODEL
Akka
Toolkit e runtime che implementano attori su JVM
26
Riccardo Cardin
(c) http://akka.io
27. Ingegneria del software mod. B
AKKA
27
Riccardo Cardin
type Receive = PartialFunction[Any, Unit]
trait Actor {
def receive: Receive // Actor actual behaviour
implicit val self: ActorRef // Reference to itself
def sender: ActorRef // Reference to the sender of last message
implicit val context: ActorContext // Execution context
}
abstract class ActorRef {
// Send primitives
def !(msg: Any)(implicit sender: ActorRef = Actor.noSender): Unit
def tell(msg: Any, sender: ActorRef) = this.!(msg)(sender)
// ...
}
trait ActorContext {
// Change behaviour of an Actor
def become(behavior: Receive, discardOld: Boolean = true): Unit
def unbecome(): Unit
// Create a new Actor
def actorOf(p: Props, name: String): ActorRef
def stop(a: ActorRef): Unit // Stop an Actor
// ...
}
scala
28. Ingegneria del software mod. B
AKKA
Esempio
Implementazione di un contatore con gli attori
28
Riccardo Cardin
class Counter extends Actor {
// State == explicit behaviour
def counter(n: Int): Receive = {
// Receive two types of messages: „incr‟ and „get‟
// „incr‟ change actor‟s behaviour
case ”incr” => context.become(counter(n + 1))
// „get‟ returns current counter value to sender
case ”get” => sender ! n
}
def receive = counter(0) // Default behaviour
}
scala
a:[0]
a!incr
a:[1]
a!incr
a:[2]
a!get sender!2
È possibile
modellare
anche stati
interni
29. Ingegneria del software mod. B
AKKA
Resilienza
Contenimento e riposte automatiche agli errori
Gli attori in errore vengono terminati o riavviati
La decisione è presa da un attore supervisore
Gli attori con supervisione formano una struttura ad albero
Il supervisore crea i suoi subordinati
29
Riccardo Cardin
class Manager extends Actor {
// OneForOneStrategy restarts only actor which died
override val supervisorStrategy = OneForOneStrategy() {
case _: DBException => Restart // reconnect to DB
case _: ActorKilledException => Stop
case _: ServiceDownException => Escalate
}
// ...
context.actorOf(Props[DBActor], ”db”)
// ...
}
sup
sub1
sub2
sub1.1
Errorflow
30. Ingegneria del software mod. B
AKKA
Actor lifecycle
30
Riccardo Cardin
Start
Restart
Stop
new Actor
preStart
fail
preStart
new Actor
stop
postStop
Supervisor
An actor
ActorRef rimane
valido tra un riavvio
e l’altro
Le fasi di riavvio
possono essere
molteplici
31. Ingegneria del software mod. B
BIBLIOGRAFIA
The Reactive Manifesto http://www.reactivemanifesto.org/
Promises in Node.js with Q – An Alternative to Callbacks
http://strongloop.com/strongblog/promises-in-node-js-with-q-
an-alternative-to-callbacks/
The Reactive Extensions (Rx) http://msdn.microsoft.com/en-
us/data/gg577609.aspx
Futures and Promises http://docs.scala-
lang.org/overviews/core/futures.html
Java concurrency (multi-threading)
http://www.vogella.com/tutorials/JavaConcurrency/article.html
#futures
Java 8: Definitive guide to CompletableFuture
http://nurkiewicz.blogspot.it/2013/05/java-8-definitive-guide-
to.html
Taming asynchronous programming
http://eamodeorubio.github.io/tamingasync/#/ 31
Riccardo Cardin
32. Ingegneria del software mod. B
BIBLIOGRAFIA
Monadic futures in Java 8
http://zeroturnaround.com/rebellabs/monadic-futures-in-java8/
Monads http://ericlippert.com/category/monads/
Callback Hell http://callbackhell.com/
CompletableFuture
http://www.slideshare.net/kojilin/completable-future
RxJava Wiki https://github.com/Netflix/RxJava/wiki
Functional Reactive Programming with RxJava
https://speakerdeck.com/benjchristensen/functional-reactive-
programming-with-rxjava-javaone-2013
Principle of Reactive Programming
https://class.coursera.org/reactive-001
Actors http://doc.akka.io/docs/akka/snapshot/scala/actors.html
32
Riccardo Cardin