An update to my talk about concurrency abstractions, including event loops (node.js and Vert.x), CSP (Go, Clojure), Futures, CPS/Dataflow (RxJava) and Actors (Erlang, Akka)
3. Reactive Design Patterns
Implications are massive, change is unavoidable
Users are demanding richer
and more personalized
experiences.
Yet, at the same time,
expecting blazing fast load
time.
Users
Mobile and HTML5; Data and
compute clouds; scaling on
demand.
Modern application
technologies are fueling the
always-on, real-time user
expectation.
Applications
Businesses are being pushed
to react to these changing
user expectations…
...and embrace
modern application
requirements.
Businesses
7. Reactive Design Patterns
Cost of Not Being Reactive
• Cost to your wallet and the environment
• No ability to recover from failure
• No ability to be responsive to our users
8. Reactive Design Patterns
Functional Programming is Key
• We want to be asynchronous and non-blocking
• We need to ensure that our data is protected without locks
• Functional programming is critical to meeting these needs
• Declarative
• Immutable
• Referentially transparent
• Pure functions that only have inputs and outputs
9. Reactive Design Patterns
Backpressure is required
• How can a system withstand variance in load?
• What does the system communicate to users of the application
when it is overwhelmed?
• Companies can lose tremendous amounts of money by not being
able to respond to users at all times
10. Reactive Design Patterns
Tools of the Trade
All code can be found at https://github.com/ReactiveDesignPatterns/Chapter-2
11. Reactive Design Patterns
Tools of the Trade: Event Loops
• Leverage green threads to provide asynchronous semantics
• The core concept of Node.js and Vert.x
• Powerful abstraction for performance and potentially scalability
• Limited with respect to resilience
• One error can take down multiple events
• Node.js can only be restarted via init.d or system.d
• Need to be able to recapture events lost, if important
• Limited with respect to communication
• Node processes can fork another process, but can't talk to it without IPC
• Callback model leads to tangled code
12. Reactive Design Patterns
Node.js Example
var http = require('http');
var counter = 0;
http.createServer(function (req, res) {
counter++;
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Sending response: ' + counter + ' via callback!n');
}).listen(8888, '127.0.0.1');
console.log('Server up on 127.0.0.1:8888, send requests!');
13. Reactive Design Patterns
Tools of the Trade: CSP
• Communicating Sequential Processes
• Decouples the sender and receiver by leveraging a "channel"
• The underlying principle behind Go's Channels and Clojure's
core.async
• Theoretically able to statically verify a deadlock will occur at
compilation time, though no popular implementation does currently
does this
• No inherent ability to send messages in a distributed environment
• No supervision for fault tolerance
14. Reactive Design Patterns
Go Example
package main
import (
"fmt"
"time"
)
func main() {
iterations := 10
myChannel := make(chan int)
go producer(myChannel, iterations)
go consumer(myChannel, iterations)
time.Sleep(500 * time.Millisecond)
}
func producer(myChannel chan int, iterations int) {
for i := 1; i <= iterations; i++ {
fmt.Println("Sending: ", i)
myChannel <- i
}
}
func consumer(myChannel chan int, iterations int) {
for i := 1; i <= iterations; i++ {
recVal := <-myChannel
fmt.Println("Received: ", recVal)
}
}
15. Reactive Design Patterns
Futures
• Allow you to define behavior that will be executed on another thread
at some time
• In some languages, responses can be handled with callbacks or
higher-order functions (map, flatMap), and can be composed into
complex interactions
• Not supervised, but do allow explicit fault tolerance via failure
callback definition
16. Reactive Design Patterns
Java8 CompletableFuture Example
package org.reactivedesignpatterns.chapter2.future;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public class ParallelRetrievalExample {
final CacheRetriever cacheRetriever;
final DBRetriever dbRetriever;
ParallelRetrievalExample(CacheRetriever cacheRetriever,
DBRetriever dbRetriever) {
this.cacheRetriever = cacheRetriever;
this.dbRetriever = dbRetriever;
}
public Object retrieveCustomer(final long id) {
final CompletableFuture<Object> cacheFuture = CompletableFuture
.supplyAsync(() -> {
return cacheRetriever.getCustomer(id);
});
final CompletableFuture<Object> dbFuture = CompletableFuture
.supplyAsync(() -> {
return dbRetriever.getCustomer(id);
});
return CompletableFuture.anyOf(cacheFuture, dbFuture);
}
}
17. Reactive Design Patterns
Tools of the Trade: CPS and Dataflow
• Take asynchronous operations and compose them into steps of
execution, like a pipeline
• Application logic looks synchronous and clean, compiled into code
that executes asynchronously
• Maintains order of execution
• Do not scale across machines
• Can be supervised, but failure handling can depend on tool you
choose
• The platform matters
• Green threads/processes (aka fibers) mean less context switching, but
also more complex management of failures
18. Reactive Design Patterns
Tools of the Trade: Reactive Extensions
(RX)
• Combine the Iterator and Observer patterns into the Observable
• Excellent mechanism for handling streams of data
• Fault tolerance depends on implementation
• Reactive Streams (http://www.reactive-streams.org/)
• Introduced the requirement for handling backpressure in overwhelmed
systems, as well as a test kit to prove compliance.
• Consortium includes Lightbend, Pivotal, Netflix, RedHat, Twitter and
more
• Interoperability is a core abstraction
19. Reactive Design Patterns
RxJava Example
package org.reactivedesignpatterns.chapter2.rxjava;
import rx.Observable;
import rx.functions.Action1;
public class RxJavaExample {
RxJavaExample() {
}
public void observe(String[] strings) {
Observable.from(strings).subscribe((s) -> {
System.out.println("Received " + s);
});
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class RxJavaExampleTest {
final RxJavaExample rxJavaExample = new RxJavaExample();
@Test
public void testRxJava() {
String[] strings = { "a", "b", "c" };
rxJavaExample.observe(strings);
}
}
20. Reactive Design Patterns
Tools of the Trade: Actors
• Individual entities that can only communicate by passing messages
• Excellent for isolating mutable state and protecting it without locks
• Location transparency
• Supervision
• Well-suited for creating state machines
• Several implementations, most popular are Erlang and Akka
• Best suited for the physical boundaries in your application
21. Reactive Design Patterns
Akka Example
class MySupervisor extends Actor {
// Define how to handle failures in a child
override def supervisorStrategy = OneForOneStrategy() {
case badJson: InvalidJsonException => {
saveInvalidJson(badJson)
Resume
}
case _: BadConnection => Escalate
case _ => Restart
}
// Create the child under me
val child = context.actorOf(Props[JsonHandlerAndPersister])
// Handle any messages I may receive
def receive = {
case _ =>
}
}
22. Reactive Design Patterns
Regardless of the
tools you choose,
find ways to build
message-driven,
elastic and resilient
applications that
are responsive to
your users