4. What are the tools?
Actors – simple & high performance concurrency
Cluster, Cluster tools – tools for building distributed
systems
Streams – reactive streams implementation
Persistence – CQRS + Event Sourcing for Actors
HTTP – fully async streaming HTTP Server
Alpakka – Reactive Streams Integrations a’la Camel
Complete Java & Scala APIs for all features
5. What are the tools?
Actors – simple & high performance concurrency
Cluster, Cluster tools – tools for building distributed
systems
Streams – reactive streams implementation
Persistence – CQRS + Event Sourcing for Actors
HTTP – fully async streaming HTTP Server
Alpakka – Reactive Streams Integrations a’la Camel
Complete Java & Scala APIs for all features
(already type-safe)
(already type-safe)
(already type-safe)
Typed
Typed
Typed
8. • mutate state (including spawning a child actor)
• send messages to other actors
• change its behavior
Actor
Message
Inbox
MessageMessage
An Actor can…
10. At the core of Akka “untyped”
Any => Unit
MyActor extends Actor
On the inside
From the outside
ActorRef
11. import akka.actor.{Actor, ActorSystem, Props}
object UntypedSample1 extends App {
case class Hello(who: String)
class GreetingActor extends Actor {
def receive = {
case Hello(who) => println(s"Hello $who!")
}
}
val props = Props(new GreetingActor)
val system = ActorSystem("my-system")
val ref = system.actorOf(props, "greeter")
ref ! Hello("Johan")
ref ! Hello("Scala Italy")
ref ! "ups"
}
12. import akka.actor.{Actor, ActorSystem, Props}
object UntypedSample1 extends App {
case class Hello(who: String)
class GreetingActor extends Actor {
def receive = {
case Hello(who) => println(s"Hello $who!")
}
}
val props = Props(new GreetingActor)
val system = ActorSystem("my-system")
val ref = system.actorOf(props, "greeter")
ref ! Hello("Johan")
ref ! Hello("Scala Italy")
ref ! "ups"
}
13. import akka.actor.{Actor, ActorSystem, Props}
object UntypedSample1 extends App {
case class Hello(who: String)
class GreetingActor extends Actor {
def receive = {
case Hello(who) => println(s"Hello $who!")
}
}
val props = Props(new GreetingActor)
val system = ActorSystem("my-system")
val ref = system.actorOf(props, "greeter")
ref ! Hello("Johan")
ref ! Hello("Scala Italy")
ref ! "ups"
}
user
my-system
greeter
14. import akka.actor.{Actor, ActorSystem, Props}
object UntypedSample1 extends App {
case class Hello(who: String)
class GreetingActor extends Actor {
def receive = {
case Hello(who) => println(s"Hello $who!")
}
}
val props = Props(new GreetingActor)
val system = ActorSystem("my-system")
val ref = system.actorOf(props, "greeter")
ref ! Hello("Johan")
ref ! Hello("Scala Italy")
ref ! "ups"
}
user
my-system
greeter
15. case class Hello(who: String)
class GreetingActor extends Actor {
def receive = {
case Hello(who) => println(s"Hello $who!")
}
}
val props = Props(new GreetingActor)
val system = ActorSystem("my-system")
val ref = system.actorOf(props, "greeter")
ref ! Hello("Johan")
ref ! Hello("Scala Italy")
ref ! "ups"
}
[info] Running UntypedSample1
Hello Johan!
Hello Scala Italy!
user
greeter
16. case class Hello(who: String)
class GreetingActor extends Actor {
def receive = {
case Hello(who) => println(s"Hello $who!")
}
}
val props = Props(new GreetingActor)
val system = ActorSystem("my-system")
val ref = system.actorOf(props, "greeter")
ref ! Hello("Johan")
ref ! Hello("Scala Italy")
ref ! "ups"
}
[info] Running UntypedSample1
Hello Johan!
Hello Scala Italy!
user
greeter
17. At the core of Akka Typed
Msg => Behavior[Msg]
Behavior[Msg]
On the inside
ActorRef[Msg]
From the outside
18. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
19. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
20. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
21. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
my-system
user (greeter)
22. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
Behavior[Hello]
ActorSystem[Hello]
23. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
ActorSystem[Hello]
ActorSystem[Hello]
== ActorRef[Hello]
24. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
25. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
[info] Running TypedSample1
Hello Johan!
Hello Scala Italy!
26. case class Hello(who: String)
val greetingBehavior: Behavior[Hello] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"Hello $who!")
Behaviors.same
}
val system =
ActorSystem(greetingBehavior, "my-system")
system ! Hello("Johan")
system ! Hello("Scala Italy")
28. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
29. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
30. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
31. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
32. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
33. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
34. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
35. sealed trait Command
case class Hello(who: String) extends Command
case class ChangeGreeting(newGreeting: String)
extends Command
def greeter(greeting: String): Behavior[Command] =
Behaviors.receiveMessage {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeter(newGreeting)
}
val system = ActorSystem(
greeter("Hello"),
"my-system")
system ! Hello("World")
system ! ChangeGreeting("Buon Pomeriggio")
system ! Hello("Scala Italy")
[info] Running TypedSample2
Hello World!
Buon Pomeriggio Scala Italy!
37. class GreetingBehavior(private var greeting: String)
extends ExtensibleBehavior[Command] {
def receive(
ctx: ActorContext[Command],
msg: Command
): Behavior[Command] = {
msg match {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeting = newGreeting
Behaviors.same
}
}
def receiveSignal(
ctx: ActorContext[Command],
msg: Signal
): Behavior[Command] = {
Behaviors.same
}
}
38. class GreetingBehavior(private var greeting: String)
extends ExtensibleBehavior[Command] {
def receive(
ctx: ActorContext[Command],
msg: Command
): Behavior[Command] = {
msg match {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeting = newGreeting
Behaviors.same
}
}
def receiveSignal(
ctx: ActorContext[Command],
msg: Signal
): Behavior[Command] = {
Behaviors.same
}
}
39. class GreetingBehavior(private var greeting: String)
extends ExtensibleBehavior[Command] {
def receive(
ctx: ActorContext[Command],
msg: Command
): Behavior[Command] = {
msg match {
case Hello(who) =>
println(s"$greeting $who!")
Behaviors.same
case ChangeGreeting(newGreeting) =>
greeting = newGreeting
Behaviors.same
}
}
def receiveSignal(
ctx: ActorContext[Command],
msg: Signal
): Behavior[Command] = {
Behaviors.same
}
}
40. More behavior factories
val behaviorWithSetup: Behavior[Unit] =
Behaviors.setup { ctx =>
ctx.log.info("Starting up")
// other startup logic, for example
// spawn children, subscribe, open resources
// then return the actual behavior used
Behaviors.empty
}
41. More behavior factories
case class Message(text: String)
val behaviorWithTimer: Behavior[Message] =
Behaviors.withTimers { timers =>
timers.startSingleTimer(
"single",
Message("single"),
1.second)
timers.startPeriodicTimer(
"repeated",
Message("repeated"),
5.seconds)
Behaviors.receive { (ctx, msg) =>
ctx.log.info("Got msg: {}", msg)
Behaviors.same
}
}
42. More behavior factories
•Behaviors.empty, Behaviors.ignore
•Behaviors.withMDC
•Behaviors.monitor, Behaviors.intercept
(intercept not in there yet, in progress, so likely)
•Behaviors.receivePartial
•Behaviors.receiveSignal -
PreRestart, PostStop, Terminated
•Behaviors.supervise
43. Burglar Alarm
• enabled/disabled with a
pin code
• accepts notifications
about “activity”
• if enabled, on activity,
sound the alarm
“Realistic” Example
DIY
44. sealed trait AlarmMessage
case object EnableAlarm extends AlarmMessage
case class DisableAlarm(pinCode: String) extends AlarmMessage
case class ActivitySeen(what: String) extends AlarmMessage
45. def disabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case EnableAlarm =>
enabledAlarm(pinCode)
case _ =>
Behaviors.unhandled
}
}
46. def disabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case EnableAlarm =>
enabledAlarm(pinCode)
case _ =>
Behaviors.unhandled
}
}
47. def disabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case EnableAlarm =>
enabledAlarm(pinCode)
case _ =>
Behaviors.unhandled
}
}
48. def enabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case ActivitySeen(what) =>
actorCtx.log.warning(
"EOEOEOE, activity detected: {}", what)
Behaviors.same
case DisableAlarm(`pinCode`) =>
actorCtx.log.info("Alarm disabled")
disabledAlarm(pinCode)
case DisableAlarm(_) =>
actorCtx.log.warning(
"OEOEOE, unauthorized disable of alarm!")
Behaviors.same
case EnableAlarm =>
Behaviors.unhandled
}
}
49. def enabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case ActivitySeen(what) =>
actorCtx.log.warning(
"EOEOEOE, activity detected: {}", what)
Behaviors.same
case DisableAlarm(`pinCode`) =>
actorCtx.log.info("Alarm disabled")
disabledAlarm(pinCode)
case DisableAlarm(_) =>
actorCtx.log.warning(
"OEOEOE, unauthorized disable of alarm!")
Behaviors.same
case EnableAlarm =>
Behaviors.unhandled
}
}
50. def enabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case ActivitySeen(what) =>
actorCtx.log.warning(
"EOEOEOE, activity detected: {}", what)
Behaviors.same
case DisableAlarm(`pinCode`) =>
actorCtx.log.info("Alarm disabled")
disabledAlarm(pinCode)
case DisableAlarm(_) =>
actorCtx.log.warning(
"OEOEOE, unauthorized disable of alarm!")
Behaviors.same
case EnableAlarm =>
Behaviors.unhandled
}
}
51. def enabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case ActivitySeen(what) =>
actorCtx.log.warning(
"EOEOEOE, activity detected: {}", what)
Behaviors.same
case DisableAlarm(`pinCode`) =>
actorCtx.log.info("Alarm disabled")
disabledAlarm(pinCode)
case DisableAlarm(_) =>
actorCtx.log.warning(
"OEOEOE, unauthorized disable of alarm!")
Behaviors.same
case EnableAlarm =>
Behaviors.unhandled
}
}
52. def enabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case ActivitySeen(what) =>
actorCtx.log.warning(
"EOEOEOE, activity detected: {}", what)
Behaviors.same
case DisableAlarm(`pinCode`) =>
actorCtx.log.info("Alarm disabled")
disabledAlarm(pinCode)
case DisableAlarm(_) =>
actorCtx.log.warning(
"OEOEOE, unauthorized disable of alarm!")
Behaviors.same
case EnableAlarm =>
Behaviors.unhandled
}
}
53. def enabledAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive { (actorCtx, msg) =>
msg match {
case ActivitySeen(what) =>
actorCtx.log.warning(
"EOEOEOE, activity detected: {}", what)
Behaviors.same
case DisableAlarm(`pinCode`) =>
actorCtx.log.info("Alarm disabled")
disabledAlarm(pinCode)
case DisableAlarm(_) =>
actorCtx.log.warning(
"OEOEOE, unauthorized disable of alarm!")
Behaviors.same
case EnableAlarm =>
Behaviors.unhandled
}
}
54. val system = ActorSystem(enabledAlarm("0000"), "alarm")
system ! ActivitySeen("door opened")
system ! DisableAlarm("1234")
system ! DisableAlarm("0000")
system ! ActivitySeen("window opened")
system ! EnableAlarm
[WARN] [19:08:45.332] EOEOEOE, activity detected:
door opened
[WARN] [09/13/2018 19:08:45.332] OEOEOE, unauthorized
disable of alarm!
[INFO] [09/13/2018 19:08:45.333] Alarm disabled
55. • Discoverability, how are things related
• More compile time type-safety, less runtime
debugging
Types helps productivity!
(and results in more reliable systems in
production)
The need for strong/strict typing
“Sending the wrong message to an
actor is actually quite uncommon”
– Myself
56. • In untyped actors the sender was
auto-magically available - sender()
• In Akka Typed the recipient of
response has to be encoded in
message (why?)
A consequence
59. ResponseFromB
Response From B is not
part of As protocol
So we can’t use “self”
Actor[ProtocolA] Actor[ProtocolB]
Request-response with other actors
RequestForB(replyTo:
ActorRef[ResponseFromB])
61. Request-response with other actors
• One off request-response with a
timeout - ask
• Fire-and-forget with (or “subscribe”)
- adapter
Two flavours:
62. One off with Ask
sealed trait ProtocolA
case class GotResult(result: String) extends ProtocolA
case class GotNoResult(why: String) extends ProtocolA
// in the actor
implicit val timeout: Timeout = 3.seconds
ctx.ask(actorB)(RequestForB.apply) {
case Success(ResponseFromB(result)) =>
GotResult(result)
case Failure(error) =>
GotNoResult(error.getMessage)
}
63. Long live the adapter
val adapter: ActorRef[ResponseFromB] =
ctx.messageAdapter {
case ResponseFromB(result) =>
GotResult(result)
}
Behaviors.receiveMessage {
case TriggerRequest =>
actorB ! RequestForB(adapter)
Behaviors.same
}
64. Distributed Systems
–Leslie Lamport
”A distributed system is one in which
the failure of a computer you didn't
even know existed can render your
own computer unusable”
65. Why is it so hard?
Reliability: power failure, old network
equipment, network congestion, coffee in
router,
rodents, that guy in the IT dept., DDOS attacks…
Latency: loopback vs local net vs shared
congested local net vs internet
Bandwidth: again loopback vs local vs shared
local vs internet
The Joys of Computer Networks:
70. Why do it, if it is so hard?
Data or processing doesn’t fit a
single machine
Many objects, that should be kept in memory.
Many not so powerful servers can be cheaper
than a supercomputer.
Elasticity
Being able to scale in (less servers) and out (more
servers) depending on load. Not paying for
servers unless you need them.
Resilience
Building systems that will keep working in the
face of failures or degrade gracefully.
71. Actor Model vs Network
Interaction already modelled as immutable messages
Data travels over the network in packages, changes has to be explicitly
sent back.
At most once
Data reaches a node on the other side at most once, but can be lost,
already part of model!
A recipient of a message can reply directly to sender
Regardless if there were intermediate recipients of the message:
A > B > C – C can reply directly to A
Messages not limited to request response
Messages can flow in either direction when two systems are connected.
79. Distributed Burglar Alarm
• we can have any number of
nodes
• a sensor on any node can
report activity to the alarm
• The node which the alarm
runs on can change
Example
DYI
Distributed
81. sealed trait AlarmMessage
case object EnableAlarm extends AlarmMessage
case class DisableAlarm(pinCode: String) extends AlarmMessage
case class ActivitySeen(what: String) extends AlarmMessage
val serviceKey = ServiceKey[ActivitySeen]("alarm")
def distributedAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.setup { ctx =>
ctx.log.info("Starting up alarm")
ctx.system.receptionist ! Register(serviceKey, ctx.self)
enabledAlarm(pinCode)
}
Registering an actor providing a service
82. sealed trait AlarmMessage
case object EnableAlarm extends AlarmMessage
case class DisableAlarm(pinCode: String) extends AlarmMessage
case class ActivitySeen(what: String) extends AlarmMessage
val serviceKey = ServiceKey[ActivitySeen]("alarm")
def distributedAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.setup { ctx =>
ctx.log.info("Starting up alarm")
ctx.system.receptionist ! Register(serviceKey, ctx.self)
enabledAlarm(pinCode)
}
Registering an actor providing a service
83. sealed trait AlarmMessage
case object EnableAlarm extends AlarmMessage
case class DisableAlarm(pinCode: String) extends AlarmMessage
case class ActivitySeen(what: String) extends AlarmMessage
val serviceKey = ServiceKey[ActivitySeen]("alarm")
def distributedAlarm(pinCode: String): Behavior[AlarmMessage] =
Behaviors.setup { ctx =>
ctx.log.info("Starting up alarm")
ctx.system.receptionist ! Register(serviceKey, ctx.self)
enabledAlarm(pinCode)
}
Registering an actor providing a service
84. sealed trait SensorMessage
case object Trigger extends SensorMessage
case class AlarmsChanged(
alarms: Set[ActorRef[Alarm.ActivitySeen]]
) extends SensorMessage
def sensor(where: String): Behavior[SensorMessage] =
Behaviors.setup { ctx =>
val adapter = ctx.messageAdapter[Listing] {
case Alarm.serviceKey.Listing(alarms) =>
AlarmsChanged(alarms)
}
val subscribe = Subscribe(Alarm.serviceKey, adapter)
ctx.system.receptionist ! subscribe
sensorWithAlarms(where, Set.empty)
}
Sensor, subscribing to alarms
85. sealed trait SensorMessage
case object Trigger extends SensorMessage
case class AlarmsChanged(
alarms: Set[ActorRef[Alarm.ActivitySeen]]
) extends SensorMessage
def sensor(where: String): Behavior[SensorMessage] =
Behaviors.setup { ctx =>
val adapter = ctx.messageAdapter[Listing] {
case Alarm.serviceKey.Listing(alarms) =>
AlarmsChanged(alarms)
}
val subscribe = Subscribe(Alarm.serviceKey, adapter)
ctx.system.receptionist ! subscribe
sensorWithAlarms(where, Set.empty)
}
Sensor, subscribing to alarms
86. sealed trait SensorMessage
case object Trigger extends SensorMessage
case class AlarmsChanged(
alarms: Set[ActorRef[Alarm.ActivitySeen]]
) extends SensorMessage
def sensor(where: String): Behavior[SensorMessage] =
Behaviors.setup { ctx =>
val adapter = ctx.messageAdapter[Listing] {
case Alarm.serviceKey.Listing(alarms) =>
AlarmsChanged(alarms)
}
val subscribe = Subscribe(Alarm.serviceKey, adapter)
ctx.system.receptionist ! subscribe
sensorWithAlarms(where, Set.empty)
}
Sensor, subscribing to alarms
87. sealed trait SensorMessage
case object Trigger extends SensorMessage
case class AlarmsChanged(
alarms: Set[ActorRef[Alarm.ActivitySeen]]
) extends SensorMessage
def sensor(where: String): Behavior[SensorMessage] =
Behaviors.setup { ctx =>
val adapter = ctx.messageAdapter[Listing] {
case Alarm.serviceKey.Listing(alarms) =>
AlarmsChanged(alarms)
}
val subscribe = Subscribe(Alarm.serviceKey, adapter)
ctx.system.receptionist ! subscribe
sensorWithAlarms(where, Set.empty)
}
Sensor, subscribing to alarms
88. sealed trait SensorMessage
case object Trigger extends SensorMessage
case class AlarmsChanged(
alarms: Set[ActorRef[Alarm.ActivitySeen]]
) extends SensorMessage
def sensor(where: String): Behavior[SensorMessage] =
Behaviors.setup { ctx =>
val adapter = ctx.messageAdapter[Listing] {
case Alarm.serviceKey.Listing(alarms) =>
AlarmsChanged(alarms)
}
val subscribe = Subscribe(Alarm.serviceKey, adapter)
ctx.system.receptionist ! subscribe
sensorWithAlarms(where, Set.empty)
}
Sensor, subscribing to alarms
89. def sensorWithAlarms(
where: String,
alarms: Set[ActorRef[Alarm.ActivitySeen]]
): Behavior[SensorMessage] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Trigger =>
val event = Alarm.ActivitySeen(where)
alarms.foreach(_ ! event)
Behaviors.same
case Sensor.AlarmsChanged(newSetOfAlarms) =>
ctx.log.info(s"New set of alarms: {}",
newSetOfAlarms)
sensorWithAlarms(where, newSetOfAlarms)
}
}
Sensor, react to alarm changes
90. def sensorWithAlarms(
where: String,
alarms: Set[ActorRef[Alarm.ActivitySeen]]
): Behavior[SensorMessage] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Trigger =>
val event = Alarm.ActivitySeen(where)
alarms.foreach(_ ! event)
Behaviors.same
case Sensor.AlarmsChanged(newSetOfAlarms) =>
ctx.log.info(s"New set of alarms: {}",
newSetOfAlarms)
sensorWithAlarms(where, newSetOfAlarms)
}
}
Sensor, react to alarm changes
91. def sensorWithAlarms(
where: String,
alarms: Set[ActorRef[Alarm.ActivitySeen]]
): Behavior[SensorMessage] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Trigger =>
val event = Alarm.ActivitySeen(where)
alarms.foreach(_ ! event)
Behaviors.same
case Sensor.AlarmsChanged(newSetOfAlarms) =>
ctx.log.info(s"New set of alarms: {}",
newSetOfAlarms)
sensorWithAlarms(where, newSetOfAlarms)
}
}
Sensor, react to alarm changes
92. def sensorWithAlarms(
where: String,
alarms: Set[ActorRef[Alarm.ActivitySeen]]
): Behavior[SensorMessage] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Trigger =>
val event = Alarm.ActivitySeen(where)
alarms.foreach(_ ! event)
Behaviors.same
case Sensor.AlarmsChanged(newSetOfAlarms) =>
ctx.log.info(s"New set of alarms: {}",
newSetOfAlarms)
sensorWithAlarms(where, newSetOfAlarms)
}
}
Sensor, react to alarm changes
93. Cluster config
val config = ConfigFactory.parseString(
"""
akka.actor.provider=cluster
akka.remote.artery.enabled=true
akka.remote.artery.transport=tcp
# get a port from the OS
akka.remote.artery.canonical.port=0
akka.remote.artery.canonical.host=127.0.0.1
# these are ok just because this is a sample
akka.cluster.jmx.multi-mbeans-in-same-jvm=on
akka.actor.warn-about-java-serializer-usage=off
""")
94. Programmatic cluster startup
val system1 = ActorSystem(
distributedAlarm(pinCode = "0000"), "alarm", config)
val system2 = ActorSystem(sensor("window"), "alarm", config)
val system3 = ActorSystem(sensor("door"), "alarm", config)
// Note: in a real system you would not likely
// run multiple nodes in the same JVM
val node1 = Cluster(system1)
val node2 = Cluster(system2)
val node3 = Cluster(system3)
// node 1 joins itself to form cluster
node1.manager ! Join(node1.selfMember.address)
// the other two joins that cluster
node2.manager ! Join(node1.selfMember.address)
node3.manager ! Join(node1.selfMember.address)
95. Programmatic cluster startup
val system1 = ActorSystem(
distributedAlarm(pinCode = "0000"), "alarm", config)
val system2 = ActorSystem(sensor("window"), "alarm", config)
val system3 = ActorSystem(sensor("door"), "alarm", config)
// Note: in a real system you would not likely
// run multiple nodes in the same JVM
val node1 = Cluster(system1)
val node2 = Cluster(system2)
val node3 = Cluster(system3)
// node 1 joins itself to form cluster
node1.manager ! Join(node1.selfMember.address)
// the other two joins that cluster
node2.manager ! Join(node1.selfMember.address)
node3.manager ! Join(node1.selfMember.address)
96. Programmatic cluster startup
val system1 = ActorSystem(
distributedAlarm(pinCode = "0000"), "alarm", config)
val system2 = ActorSystem(sensor("window"), "alarm", config)
val system3 = ActorSystem(sensor("door"), "alarm", config)
// Note: in a real system you would not likely
// run multiple nodes in the same JVM
val node1 = Cluster(system1)
val node2 = Cluster(system2)
val node3 = Cluster(system3)
// node 1 joins itself to form cluster
node1.manager ! Join(node1.selfMember.address)
// the other two joins that cluster
node2.manager ! Join(node1.selfMember.address)
node3.manager ! Join(node1.selfMember.address)
97. Simulate some activity
// at some point in time
system2 ! Sensor.Trigger
system3 ! Sensor.Trigger
…
[INFO] [17:48:27.456] New set of alarms: Set(Actor[akka://
alarm-system@127.0.0.1:54960/user#0])
[INFO] [17:48:27.511] New set of alarms: Set(Actor[akka://
alarm-system@127.0.0.1:54960/user#0])
[WARN] [17:48:29.021] EOEOEOE, activity detected: window
[WARN] [17:48:31.011] EOEOEOE, activity detected: door
98. Summary
• On message - message another actor,
change state or behavior
• Types gives better guardrails and makes
understanding the system easier
• We can use the same abstraction for
local and distributed
99. How ready is it?
• APIs not frozen (normally we keep binary
compatibility in minor releases) - we expect
only minor changes though
• Pretty much the same since mid spring 2018
• Most likely to change - Typed Persistence
• All cluster APIs and Cluster tools covered
• Working towards a GA, hopefully this fall
100. GitHub repo with all samples in this talk
https://git.io/fAiZt
Akka typed docs
doc.akka.io/docs/akka/current/typed
More resources
blog.akka.io - news and articles
discuss.akka.io - forums
developer.lightbend.com - samples
Further reading