Reactive Reference Architecture

Reactive Reference
Architectures
Nilanjan Raychaudhuri
@nraychaudhuri
Markus Jura
@markusjura
Massive Learning Platform
Big data
http://allthingsd.com/files/2013/07/minions-­‐640x327.jpg
Principles of Reactive Systems
3
Architecture
Web (Play)
backend (Akka)
Journal (Cassandra)
Fast data processing (Spark)
4
http://cdn1.ssninsider.com/wp-­‐content/uploads/2013/08/despicable-­‐me-­‐2-­‐minions-­‐e1375818315418.png
Problem: Splitting application
Monolithic
7
Splitting MLP
Frontend nodes
Backend nodes
8
Akka Distributed Pub/Sub
• Location transparency
• Publish/Subscribe
• Dynamically add/remove
9
Distributed Pub/Sub Mediator
Frontend nodes
M
M
M
M
M
Backend nodes
10
Code
Frontend
// Starting up the mediator
val mediator = DistributedPubSubExtension(system).mediator
// Sending message through mediator
mediator ? DistributedPubSubMediator.Send(“/user/course-aggregate”,
CreateCourse(course),
localAffinity = true)
12
Backend
// Starting up the mediator
val mediator = DistributedPubSubExtension(system).mediator
// Setting up the services
val courseAggregate =
system.actorOf(CourseRepository.props, "course-aggregate")
mediator ! DistributedPubSubMediator.Put(courseAggregate)
val levelAggregate =
system.actorOf(LevelRepository.props, "level-aggregate")
mediator ! DistributedPubSubMediator.Put(levelAggregate)
13
http://cdn1.ssninsider.com/wp-­‐content/uploads/2013/08/despicable-­‐me-­‐2-­‐minions-­‐e1375818315418.png
Problem: Distributed State
State on single node
Frontend nodes
Backend nodes
16
State across nodes
Frontend nodes
Backend nodes
17
Akka Cluster Sharding
• Partition actors across Cluster
• Location transparency
• Rebalancing
18
How does it work?
Shard Coordinator
Shard Region 1
Shard Region 2
19
Code
Backend: Create ShardRegion
// Backend: Start cluster sharing in non-proxy mode
ClusterSharding(system).start(
"users",
Some(User.props),
UserSharding.idExtractor, UserSharding.shardResolver())
21
Backend: Shard Resolver & Id Extractor
// Extract the user shard
// Rule of thumb: 10 times the max numbers of sharding nodes
def shardResolver(shardCount: Int = 20) = {
case UserService.Envelope(email, _) =>
email.hashCode % shardCount.toString
}
// Extract the id and payload of the user actor
val idExtractor: ShardRegion.IdExtractor = {
case UserService.Envelope(email, payload) =>
(email, payload)
}
22
Frontend
// Start sharding in proxy mode on every frontend node
val shardRegion: ActorRef = ClusterSharding(system).start(
"users",
None,
UserSharding.idExtractor, UserSharding.shardResolver())
// Send message via shard region
shardRegion ? UserService.Envelope(email, GetUser(email))
23
Problem: Loosing State
State in memory
Frontend nodes
Backend nodes
25
Persist state
Frontend Backend 26
Akka Persistence
• Stores state in Journal DB
• Event Sourcing
• Restore state
27
Code
Write user state
class User extends PersistentActor {
// User id / sharding extractor id
override def persistenceId: String = self.path.name
// Persist UserCreated event to Cassandra journal
override def receiveCommand: Receive = {
case UserService.CreateUser(userProgress) =>
persist(UserCreated(userProgress)) { userCreated =>
// Update user state
onUserCreated(userCreated)
sender() ! UserService.UserCreated
}
}
...
29
Recover user state
...
// Replay UserCreated message to recover state
override def receiveRecover: Receive = {
case e: UserCreated => onUserCreated(e)
}
30
http://cdn1.ssninsider.com/wp-­‐content/uploads/2013/08/despicable-­‐me-­‐2-­‐minions-­‐e1375818315418.png
Problem: Replicate Cache
Singleton Cache
Frontend nodes
C
Backend nodes
33
What is CRDT?
Conflict-free replicated data type is a type
of specially designed data structure used
to achieve strong eventual consistency
Great talk on CRDT by Sean Cribbs: Eventually Consistent Data Structures 34
CvRDT
35
CvRDT - Example
36
CmRDT
37
Akka Data replication
• CRDT for Akka cluster
• Replicated in-memory data store
• Supports many types: (GSet, ORSet, PNCounter…)
38
CRDT Replicator
Frontend nodes
C
R
R
R
R
C
R
Backend nodes
39
Code
Frontend
//starting up the replicator
val replicator = DataReplication(system).replicator
//get cache entry
val f = replicator ? Get(key, ReadLocal, None)
41
Backend
//starting up the replicator
val replicator = DataReplication(system).replicator
//updating the cache
replicator ! Update(Course.cacheKey, GSet.empty, WriteLocal)
(_ + courseCreated.course)
42
Q & Option[A]
@nraychaudhuri
@markusjura
frontend
React.js
REST API
Cache
Actors
backend
PubSub
Cluster Sharding
Persistence
CRDT
journal
44
©Typesafe 2014 – All Rights Reserved