Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Next generation actors with Akka

2.167 Aufrufe

Veröffentlicht am

Slides from the Scala italy 2018 talk about the new Akka Typed actor APIs

Veröffentlicht in: Technologie
  • Loggen Sie sich ein, um Kommentare anzuzeigen.

Next generation actors with Akka

  1. 1. Akka Typed Johan Andrén, Akka Team Scala Italy, 2018-09-14 Next generation actors with Akka
  2. 2. Johan Andrén Akka Team Stockholm Scala User Group @apnylle markatta.com/codemonkey/ johan.andren@lightbend.com
  3. 3. Build powerful reactive, concurrent, and distributed applications more easily Akka
  4. 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. 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
  6. 6. What problem are we solving?
  7. 7. Actor Message Inbox MessageMessage actorRef ! message actorRef.tell(message) Akka Actor fundamentals
  8. 8. • mutate state (including spawning a child actor) • send messages to other actors • change its behavior Actor Message Inbox MessageMessage An Actor can…
  9. 9. MVS (Minimum Viable Sample) Na-na na-na na-na na-naSample-time! Also known as “hello world”
  10. 10. At the core of Akka “untyped” Any => Unit MyActor extends Actor On the inside From the outside ActorRef
  11. 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. 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. 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. 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. 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. 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. 17. At the core of Akka Typed Msg => Behavior[Msg] Behavior[Msg] On the inside ActorRef[Msg] From the outside
  18. 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. 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. 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. 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. 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. 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. 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. 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. 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")
  27. 27. Let’s do another one right away The mystery of the changing state 🔎
  28. 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. 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. 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. 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. 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. 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. 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. 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!
  36. 36. But I don’t like it (the FP style, that is)
  37. 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. 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. 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. 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. 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. 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. 43. Burglar Alarm • enabled/disabled with a pin code • accepts notifications about “activity” • if enabled, on activity, sound the alarm “Realistic” Example DIY
  44. 44. sealed trait AlarmMessage case object EnableAlarm extends AlarmMessage case class DisableAlarm(pinCode: String) extends AlarmMessage case class ActivitySeen(what: String) extends AlarmMessage
  45. 45. def disabledAlarm(pinCode: String): Behavior[AlarmMessage] = Behaviors.receive { (actorCtx, msg) => msg match { case EnableAlarm => enabledAlarm(pinCode) case _ => Behaviors.unhandled } }
  46. 46. def disabledAlarm(pinCode: String): Behavior[AlarmMessage] = Behaviors.receive { (actorCtx, msg) => msg match { case EnableAlarm => enabledAlarm(pinCode) case _ => Behaviors.unhandled } }
  47. 47. def disabledAlarm(pinCode: String): Behavior[AlarmMessage] = Behaviors.receive { (actorCtx, msg) => msg match { case EnableAlarm => enabledAlarm(pinCode) case _ => Behaviors.unhandled } }
  48. 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. 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. 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. 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. 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. 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. 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. 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. 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
  57. 57. RequestForB ResponseFromB Request-response with other actors
  58. 58. Actor[ProtocolA] Actor[ProtocolB] RequestForB ResponseFromB A sender() couldn’t know the type! Request-response with other actors
  59. 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])
  60. 60. RequestForB(replyTo: ActorRef[ResponseFromB]) ResponseFromB ? ResponseFromB => ProtocolA ProtocolA Request-response with other actors Actor[ProtocolA] Actor[ProtocolB]
  61. 61. Request-response with other actors • One off request-response with a timeout - ask • Fire-and-forget with (or “subscribe”) - adapter Two flavours:
  62. 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. 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. 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. 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:
  66. 66. Partial Failure ✂Node 1 Node 2 request response
  67. 67. Partial Failure ✂Node 1 Node 2 request💥
  68. 68. Partial Failure ✂Node 1 Node 2 request 💥
  69. 69. Partial Failure ✂Node 1 Node 2 request respons 💥
  70. 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. 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.
  72. 72. Local ActorSystem Message Message Actor Actor Actor
  73. 73. JVMJVM Distributed ActorSystemActorSystem Message Message Actor Actor Actor
  74. 74. Akka Cluster + Akka Typed ActorSystem ActorSystem ActorSystem
  75. 75. Receptionist Typed Receptionist Receptionist Receptionist ActorRef[AlarmMessage] Sensor-Actor Subscribe(key, actorRef)
  76. 76. Receptionist Typed Receptionist Receptionist Receptionist ActorRef[AlarmMessage] Sensor-Actor Register(Key[AlarmMessage], ActorRef[AlarmMessage])
  77. 77. Receptionist Typed Receptionist Receptionist Receptionist ActorRef[AlarmMessage] Sensor-Actor Register(Key[AlarmMessage], ActorRef[AlarmMessage]) Listing(key, actorRefs)
  78. 78. Receptionist Typed Receptionist Receptionist Receptionist ActorRef[AlarmMessage] Sensor-Actor Listing(key, actorRefs) ActorRef[AlarmMessage]AlarmMessage
  79. 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
  80. 80. Sensor-Actor Sensor-Actor ActorRef[AlarmMessage] AlarmMessage AlarmMessage Trigger Trigger
  81. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
  101. 101. Questions? GitHub repo with all samples in this talk https://git.io/fAiZt
  102. 102. http://akka.io @apnylle johan.andren@lightbend.com Thanks for listening!

×