Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Akka Typed (quick talk) - JFokus 2018

942 views

Published on

In this talk we explain the basics of Typed Actors as they are to land in Akka as a stable module in 2018. Typed Actors ("Akka Typed") re-introduce typesafety to concurrency and distributed systems thanks to the abstraction of a typed actor reference.

Published in: Engineering
  • Be the first to comment

Akka Typed (quick talk) - JFokus 2018

  1. 1. Type Safe Actors Konrad Malawski, Akka Team(Johan Andrén got sick…) JFokus, Stockholm, 2018-02-07 Next generation with Akka
  2. 2. Konrad `ktoso` Malawski Akka Team Reactive Streams TCK Scala Library Improvement Process Committee Member akka.io typesafe.com geecon.org Java.pl / KrakowScala.pl sckrk.com / meetup.com/Paper-Cup @ London GDGKrakow.pl lambdakrk.pl
  3. 3. Build powerful reactive, concurrent, and distributed applications more easily Akka
  4. 4. 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 What’s in the toolkit?
  5. 5. Actor Message Inbox MessageMessage Akka Actor Fundamentals actorRef.tell(message, sender) actorRef ! message // sender captured implicitly
  6. 6. • mutate state (including spawning a child actor) • send messages to other actors • change its behavior For a message an Actor Actor Message Inbox MessageMessage can
  7. 7. Untyped Actors => Akka Typed Untyped Actors • “sender()” propagated automatically • any message type can be sent to any Actor • actor’s def receive = { … } don’t return values
  8. 8. Untyped Actors => Akka Typed Untyped Actors • “sender()” propagated automatically • any message type can be sent to any Actor • actor’s def receive = { … } don’t return values Akka Typed Actors • “sender” is part of the protocol • only the right type of messages can be sent: 
 ActorRef[MessageProtocol] • behaviors always return new behaviors
 (state machines for the win)
  9. 9. Quick reminder (Untyped): class MyActor extends Actor { def receive = { case Msg(a) => sender() ! “Hi there!” // … } }
  10. 10. Sample Burglar Alarm • enabled/disabled with a pin code • accepts notifications about “activity” • if enabled on activity, sound the alarm
  11. 11. // message protocol trait AlarmMessage case class EnableAlarm(pinCode: String) extends AlarmMessage case class DisableAlarm(pinCode: String) extends AlarmMessage case object ActivityEvent extends AlarmMessage
  12. 12. // behavior def enabled(pinCode: String): Behavior[AlarmMessage] = Behaviors.immutable[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.immutable[AlarmMessage] { (ctx, msg) => msg match { case EnableAlarm(enteredCode) if enteredCode == pinCode => enabled(pinCode) // change behavior case _ => Behaviors.ignore } }
  13. 13. // behavior def enabled(pinCode: String): Behavior[AlarmMessage] = Behaviors.immutable[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.immutable[AlarmMessage] { (ctx, msg) => msg match { case EnableAlarm(enteredCode) if enteredCode == pinCode => enabled(pinCode) // change behavior case _ => Behaviors.ignore } }
  14. 14. // behavior def enabled(pinCode: String): Behavior[AlarmMessage] = Behaviors.immutable[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.immutable[AlarmMessage] { (ctx, msg) => msg match { case EnableAlarm(enteredCode) if enteredCode == pinCode => enabled(pinCode) // change behavior case _ => Behaviors.ignore } }
  15. 15. // behavior def enabled(pinCode: String): Behavior[AlarmMessage] = Behaviors.immutable[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.immutable[AlarmMessage] { (ctx, msg) => msg match { case EnableAlarm(enteredCode) if enteredCode == pinCode => enabled(pinCode) // change behavior case _ => Behaviors.ignore } }
  16. 16. // behavior def enabled(pinCode: String): Behavior[AlarmMessage] = Behaviors.immutable[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.immutable[AlarmMessage] { (ctx, msg) => msg match { case EnableAlarm(enteredCode) if enteredCode == pinCode => enabled(pinCode) // change behavior case _ => Behaviors.ignore } }
  17. 17. // 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")
  18. 18. // 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
  19. 19. Local ActorSystem Message Message Actor Actor Actor
  20. 20. JVM 2 JVM 1 Distributed ActorSystem ActorSystem Message Message Actor Actor Actor location transparency => no code changes
  21. 21. Akka Cluster ActorSystem ActorSystem ActorSystem
  22. 22. Receptionist Receptionist Receptionist Receptionist Subscribe(key, actorRef) Register(Key[AlarmMessage], ActorRef[AlarmMessage]) ActorRef[AlarmMessage] ActorRef[SensorEvent]
  23. 23. Sample Distributed Burglar Alarm • we can have any number of nodes • a sensor on any node can report activity to the alarm
  24. 24. val receptionist = Receptionist(system).ref val alarmKey = ServiceKey[AlarmMessage]("alarm") def startAlarm(pinCode: String): Behavior[AlarmMessage] = Behaviors.deferred { ctx => receptionist ! Register(alarmKey, ctx.self, ctx.system.deadLetters) enabled(pinCode) }
  25. 25. val receptionist = Receptionist(system).ref val alarmKey = ServiceKey[AlarmMessage]("alarm") def startAlarm(pinCode: String): Behavior[AlarmMessage] = Behaviors.deferred { ctx => receptionist ! Register(alarmKey, ctx.self, ctx.system.deadLetters) enabled(pinCode) }
  26. 26. // protocol trait SensorEvent case object WindowOpened extends SensorEvent // behavior def sensorBehavior: Behavior[SensorEvent] = Behaviors.deferred { ctx => var alarms = Set.empty[ActorRef[AlarmMessage]] Receptionist(ctx.system).ref ! Receptionist.Subscribe( alarmKey, ctx.self.narrow[Listing[AlarmMessage]]) Behaviors.immutable[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]
  27. 27. // protocol trait SensorEvent case object WindowOpened extends SensorEvent // behavior def sensorBehavior: Behavior[SensorEvent] = Behaviors.deferred { ctx => var alarms = Set.empty[ActorRef[AlarmMessage]] Receptionist(ctx.system).ref ! Receptionist.Subscribe( alarmKey, ctx.self.narrow[Listing[AlarmMessage]]) Behaviors.immutable[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]
  28. 28. // protocol trait SensorEvent case object WindowOpened extends SensorEvent // behavior def sensorBehavior: Behavior[SensorEvent] = Behaviors.deferred { ctx => var alarms = Set.empty[ActorRef[AlarmMessage]] Receptionist(ctx.system).ref ! Receptionist.Subscribe( alarmKey, ctx.self.narrow[Listing[AlarmMessage]]) Behaviors.immutable[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]
  29. 29. class SensorBehavior(ctx: ActorContext[SensorProtocol]) extends MutableBehavior[SensorProtocol] { private var alarms = Set.empty[ActorRef[AlarmMessage]] private val receptionist: ActorRef[Receptionist.Command] = Receptionist(ctx.system).ref ctx.ask(receptionist)(Receptionist.Subscribe(alarmKey, _))(res AlarmListing(res.get.serviceInstances)) override def onMessage(msg: SensorProtocol): Behavior[SensorProtocol] = msg match { case AlarmListing(updatedAlarms) => // updated list of alarms known alarms = updatedAlarms this case WindowOpened => // inform all known alarms about activity alarms.foreach(_ ! ActivityEvent) this } } “Mutable”Behavior:
  30. 30. // 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 }
  31. 31. Thanks for listening! Konrad ktoso@lightbend.com Malawski http://kto.so / @ktosopl Akka docs: akka.io/docs Free O’Reilly report – bit.ly/why-reactive

×