5. 21 3 54 6
S1
S0
S2
Supervisor
Actor 0
Supervisor
Actor 1
Subordinate
Actors
6. Akka
• Toolset with actor model as its foundation
• Written in Scala
• JVM with Java and Scala API
7. Example
• User in organization
• Described by name
• Can read and change it
8. // messages
case object GetName
case class SetName(name: String)
// actor
class User(var name: String) extends Actor {
override def receive: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => name = newName
}
}
Plain actor
9. // messages
case object GetName
case class SetName(name: String)
// actor
class User(var name: String) extends Actor {
override def receive: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => name = newName
}
}
Plain actor
10. // messages
case object GetName
case class SetName(name: String)
// actor
class User(var name: String) extends Actor {
override def receive: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => name = newName
}
}
Plain actor
11. Persisting state
• Event sourcing
• Supported with persistence module
• Cassandra, with support for relational databases, mongo…
12. Persistent actor
// commands
case object GetName
case class SetName(name: String)
// events
case class NameSet(name: String)
class User(id: UUID) extends PersistentActor {
var name: String = null
override def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
override def receiveCommand: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name)
}
override def persistenceId: String = s"user:${id.toString}"
}
13. // commands
case object GetName
case class SetName(name: String)
// events
case class NameSet(name: String)
class User(id: UUID) extends PersistentActor {
var name: String = null
override def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
override def receiveCommand: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name)
}
override def persistenceId: String = s"user:${id.toString}"
}
Persistent actor
14. // commands
case object GetName
case class SetName(name: String)
// events
case class NameSet(name: String)
class User(id: UUID) extends PersistentActor {
var name: String = null
override def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
override def receiveCommand: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name)
}
override def persistenceId: String = s"user:${id.toString}"
}
Persistent actor
15. // commands
case object GetName
case class SetName(name: String)
// events
case class NameSet(name: String)
class User(id: UUID) extends PersistentActor {
var name: String = null
override def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
override def receiveCommand: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name)
}
override def persistenceId: String = s"user:${id.toString}"
}
Persistent actor
16. // commands
case object GetName
case class SetName(name: String)
// events
case class NameSet(name: String)
class User(id: UUID) extends PersistentActor {
var name: String = null
override def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
override def receiveCommand: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name)
}
override def persistenceId: String = s"user:${id.toString}"
}
Persistent actor
17. // commands
case object GetName
case class SetName(name: String)
// events
case class NameSet(name: String)
class User(id: UUID) extends PersistentActor {
var name: String = null
override def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
override def receiveCommand: Receive = {
case GetName => sender() ! name
case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name)
}
override def persistenceId: String = s"user:${id.toString}"
}
Persistent actor
20. Sharded actor
// commands
trait UserMsg {val id: UUID}
case class SetName(id: UUID, name: String) extends UserMsg
case class GetName(id: UUID) extends UserMsg
// events
case class NameSet(name: String)
class User extends PersistentActor {
var name: String = null
def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
def receiveCommand: Receive = {
case GetName(_) => sender() ! name
case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name)
}
def persistenceId: String = s"user:persistence:${self.path.name}"
}
21. Sharded actor
// commands
trait UserMsg {val id: UUID}
case class SetName(id: UUID, name: String) extends UserMsg
case class GetName(id: UUID) extends UserMsg
// events
case class NameSet(name: String)
class User extends PersistentActor {
var name: String = null
def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
def receiveCommand: Receive = {
case GetName(_) => sender() ! name
case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name)
}
def persistenceId: String = s"user:persistence:${self.path.name}"
}
22. Sharded actor
// commands
trait UserMsg {val id: UUID}
case class SetName(id: UUID, name: String) extends UserMsg
case class GetName(id: UUID) extends UserMsg
// events
case class NameSet(name: String)
class User extends PersistentActor {
var name: String = null
def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
def receiveCommand: Receive = {
case GetName(_) => sender() ! name
case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name)
}
def persistenceId: String = s"user:persistence:${self.path.name}"
}
23. Sharded actor
object User {
val NAME = "user"
val extractEntityId: ShardRegion.ExtractEntityId = {
case command: UserMsg => (command.id.toString, command)
}
val numberOfShards = 100
val extractShardId: ShardRegion.ExtractShardId = {
case command: UserMsg => (command.id.toString.hashCode %
numberOfShards).toString
}
}
24. Optimize for scaling
• Command query responsibility segregation
• Different databases, eventually consistent
25. trait UserMsg {val id: UUID}
// commands
case class SetName(id: UUID, name: String) extends UserMsg
// events
case class NameSet(name: String)
class UserProcessor extends PersistentActor {
var name: String = null
override def receiveRecover: Receive = {
case NameSet(changeName: String) => name = changeName
}
def receiveCommand: Receive = {
case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name)
}
def persistenceId: String = s"user:cqrs:${self.path.name}"
}
CQRS actors
26. CQRS actors
// query
case class GetName(id: UUID) extends UserMsg
class UserView extends Actor with Stash {
var name: String = null
def receive: Receive = {
case GetName(id: UUID) => stash()
case EventEnvelope(_, _, _, NameSet(newName: String)) =>
name = newName
unstashAll()
context.become(active)
sender() ! Done
}
def active: Receive = {
case GetName(_) => sender() ! name
case EventEnvelope(_, _, _, NameSet(newName: String)) =>
name = newName
sender() ! Done
}
}
27. Linking write and read
• Persistence query, API for streaming events
• Akka streams and reactive streams
• Streaming events from journal to any read database