4. • Distributed programming made simple
• High performance messaging (up to 1 million msgs/s over network on Artery,
close to 40-50 million msgs/s locally)
• Asynchronous and Reactive by default
• Initiator of Reactive Streams initiative, now part of JDK9
• Productive development, with tuned type-safe tools
What does Akka solve?
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
6. 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.
All modules ready for preview, final polish during Q2 2018
Typed
Typed
Typed
(already type-safe)
(already type-safe)
(already type-safe)
What is coming soon?
8. • mutate state (including spawning a child actor)
• send messages to other actors
• change its behavior
Actor
Message
Inbox
MessageMessage
An Actor can…
11. class MyActor extends Actor {
def receive: Any => Unit = {
case Msg(a) =>
sender() ! “Hi there!”
}
}
Reminder of Akka “Classic” Actors
12. • 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, earlier this week
13. • Not a trivial problem: Topic of multiple research papers
to bring type-safety to the Actor model
•“TAkka” 2014, Phillip Wadler et al, University of Edinburgh
•“Reactors, Channels and Event Streams …” 2015,
Aleksandar Prokopec, Martin Odersky, EPFL
•Typed Actors [RIP]
The need for strong/strict typing
15. Burglar Alarm
• enabled/disabled with a pin code
• accepts notifications about “activity”
• if enabled on activity, sound the alarm
Example
DIY
16. // message protocol
trait AlarmMessage
case class EnableAlarm(pinCode: String) extends AlarmMessage
case class DisableAlarm(pinCode: String) extends AlarmMessage
case object ActivityEvent extends AlarmMessage
17. // behavior
def enabled(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive[AlarmMessage] { (ctx, msg) =>
msg match {
case DisableAlarm(enteredCode) if enteredCode == pinCode =>
ctx.log.info("Disabling alarm")
disabled(pinCode) // change behavior
case ActivityEvent =>
ctx.log.warning("OEOEOEOEOEOE alarm, activity detected!")
Behaviors.same
case _ =>
Behaviors.ignore
}
}
def disabled(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive[AlarmMessage] {
case EnableAlarm(enteredCode) if enteredCode == pinCode =>
enabled(pinCode) // change behavior
case _ =>
Behaviors.ignore
}
18. // behavior
def enabled(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive[AlarmMessage] { (ctx, msg) =>
msg match {
case DisableAlarm(enteredCode) if enteredCode == pinCode =>
ctx.log.info("Disabling alarm")
disabled(pinCode) // change behavior
case ActivityEvent =>
ctx.log.warning("OEOEOEOEOEOE alarm, activity detected!")
Behaviors.same
case _ =>
Behaviors.ignore
}
}
def disabled(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive[AlarmMessage] {
case EnableAlarm(enteredCode) if enteredCode == pinCode =>
enabled(pinCode) // change behavior
case _ =>
Behaviors.ignore
}
19. // behavior
def enabled(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive[AlarmMessage] { (ctx, msg) =>
msg match {
case DisableAlarm(enteredCode) if enteredCode == pinCode =>
ctx.log.info("Disabling alarm")
disabled(pinCode) // change behavior
case ActivityEvent =>
ctx.log.warning("OEOEOEOEOEOE alarm, activity detected!")
Behaviors.same
case _ =>
Behaviors.ignore
}
}
def disabled(pinCode: String): Behavior[AlarmMessage] =
Behaviors.receive[AlarmMessage] {
case EnableAlarm(enteredCode) if enteredCode == pinCode =>
enabled(pinCode) // change behavior
case _ =>
Behaviors.ignore
}
20. // running it
val system = ActorSystem(enabled("0000"), "AlarmSystem")
// system is also reference to top level actor
val alarmRef: ActorRef[AlarmMessage] = system
alarmRef ! DisableAlarm("1234")
alarmRef ! ActivityEvent
alarmRef ! DisableAlarm("0000")
alarmRef ! ActivityEvent
alarmRef ! EnableAlarm("0000")
21. // running it
val system = ActorSystem(enabled("0000"), "AlarmSystem")
// system is also reference to top level actor
val alarmRef: ActorRef[AlarmMessage] = system
alarmRef ! DisableAlarm("1234")
alarmRef ! ActivityEvent
alarmRef ! DisableAlarm("0000")
alarmRef ! ActivityEvent
alarmRef ! EnableAlarm(“0000")
alarmRef ! ”0000” // compile error
Compile-time safety
22. Untyped Actors
Untyped Actors vs Akka Typed
Akka Typed Actors
• sender() propagated
automatically
• any message type can
be sent to any Actor
• actor returns nothing
upon processing a
message
• sender is optionally part
of the protocol
• only the right type of messages
can be sent:
ActorRef[MessageProtocol]
• behaviors always return the
next behavior
24. Distributed Burglar Alarm
• we can have any number of
nodes
• a sensor on any node can
report activity to the alarm
Example, iteration 2
DYI
Distributed
28. // protocol
trait SensorEvent
case object WindowOpened extends SensorEvent
// behavior
def sensorBehavior: Behavior[SensorEvent] =
Behaviors.setup { ctx =>
var alarms = Set.empty[ActorRef[AlarmMessage]]
Receptionist(ctx.system).ref ! Receptionist.Subscribe(
alarmKey, ctx.self.narrow[Listing[AlarmMessage]])
Behaviors.receive[Any]((ctx, msg) =>
msg match {
case Listing(_, updatedAlarms: Set[ActorRef[AlarmMessage]]) =>
// updated list of alarms known
alarms = updatedAlarms
Behaviors.same
case WindowOpened =>
// inform all known alarms about activity
alarms.foreach(_ ! ActivityEvent)
29. // protocol
trait SensorEvent
case object WindowOpened extends SensorEvent
// behavior
def sensorBehavior: Behavior[SensorEvent] =
Behaviors.setup { ctx =>
var alarms = Set.empty[ActorRef[AlarmMessage]]
Receptionist(ctx.system).ref ! Receptionist.Subscribe(
alarmKey, ctx.self.narrow[Listing[AlarmMessage]])
Behaviors.receive[Any]((ctx, msg) =>
msg match {
case Listing(_, updatedAlarms: Set[ActorRef[AlarmMessage]]) =>
// updated list of alarms known
alarms = updatedAlarms
Behaviors.same
case WindowOpened =>
// inform all known alarms about activity
alarms.foreach(_ ! ActivityEvent)
30. def sensorBehavior: Behavior[SensorEvent] =
Behaviors.setup { ctx =>
var alarms = Set.empty[ActorRef[AlarmMessage]]
Receptionist(ctx.system).ref ! Receptionist.Subscribe(
alarmKey, ctx.self.narrow[Listing[AlarmMessage]])
Behaviors.receive[Any]((ctx, msg) =>
msg match {
case Listing(_, updatedAlarms: Set[ActorRef[AlarmMessage]]) =>
// updated list of alarms known
alarms = updatedAlarms
Behaviors.same
case WindowOpened =>
// inform all known alarms about activity
alarms.foreach(_ ! ActivityEvent)
Behaviors.same
}
)
}.narrow // narrow Behavior[Any] down to Behavior[SensorEvent]
31. // running it
val system1 = ActorSystem(startAlarm("0000"), "AlarmSystem")
val system2 = ActorSystem(sensorBehavior, "AlarmSystem")
// programmatic cluster formation
val node1 = Cluster(system1)
val node2 = Cluster(system2)
// node1 joins itself to form cluster
node1.manager ! Join(node1.selfMember.address)
// node2 joins the now existing cluster
node2.manager ! Join(node1.selfMember.address)
// a bit later the burglar comes
after(1.day) {
system2 ! WindowOpened
}
39. SourceRef sending side
Scala
class SendingActor extends Actor {
import context.dispatcher
implicit val mat = ActorMaterializer()(context)
val numbersSource = Source(1 to 100000)
def receive = {
case SendMeNumbers ⇒
val ref: Future[SourceRef[Int]] =
numbersSource.runWith(StreamRefs.sourceRef())
val reply: Future[SendMeNumbersReply] =
ref.map(ref => SendMeNumbersReply(ref))
reply pipeTo sender()
}
}
40. SourceRef receiving side
Scala
class ReceivingActor(sendingActor: ActorRef) extends Actor {
sendingActor ! SendMeNumbers
override def receive: Receive = {
case SendMeNumbersReply(sourceRef) =>
sourceRef.runWith(Sink.foreach(println))
}
}
41. StreamRefs — new feature since Akka 2.5.10
By design NOT a“distributed automatic stream
processing deployment and management framework”.
(e.g. how Spark, Flink or Beam could be described).
Ideal for
combining Alpakka Enterprise Integration
Data Processing or Ingestion Pipelines between ActorSystems