akka & pekko/actors

Messages and Behavior

wefree 2023. 7. 17. 13:23
ref ! s"Forwards: $message"

1. messages can be of any type

  • messages must be IMMUTABLE
  • messages must be SERIALIZABLE (in practice use case class and case objects
import akka.actor.{Actor, ActorSystem, Props}

object TestActor {
  def main(args: Array[String]): Unit = {
    val actorSystem: ActorSystem = ActorSystem(name = "actorCapabilitiesDemo")

    class SimpleActor extends Actor {
      override def receive: Receive = {
        case message: String => println(s"String message: $message")
        case number: Int     => println(s"Int number: $number")
      }
    }

    val simpleActor = actorSystem.actorOf(Props[SimpleActor], "simpleActor")
    simpleActor ! "A"
    simpleActor ! 1
  }
}

 

2. actors have information about their context and about themselves

  • context.self == `this` in OOP
  • context.self 대신 self (aliased) 사용 가능
import akka.actor.{Actor, ActorSystem, Props}

object TestActor {
  def main(args: Array[String]): Unit = {
    val actorSystem: ActorSystem = ActorSystem(name = "actorCapabilitiesDemo")

    class SimpleActor extends Actor {
      override def receive: Receive = {
        case message: String =>
          println(s"[${context.self}] String message: $message")
          self ! message.toInt

        case number: Int =>
          println(s"[${self.path}] Int number: $number")
      }
    }

    val simpleActor = actorSystem.actorOf(Props[SimpleActor], "simpleActor")
    simpleActor ! "1"
  }
}

결과

[Actor[akka://actorCapabilitiesDemo/user/simpleActor#969381549]] String message: 1
[akka://actorCapabilitiesDemo/user/simpleActor] Int number: 1

 

3. actors can REPLY to messages

  • context.sender() 활용
package com.github.windbird123

import akka.actor.{Actor, ActorRef, ActorSystem, Props}

object TestActor {
  def main(args: Array[String]): Unit = {
    val actorSystem: ActorSystem = ActorSystem(name = "actorCapabilitiesDemo")

    class SimpleActor extends Actor {
      override def receive: Receive = {
        case "Hello" =>
          println(s"[${context.sender()} --> $self] Hello")

        case message: String =>
          println(s"[${context.sender()} --> $self] String message: $message")
          context.sender() ! "Hello"

        case SayHiTo(ref) =>
          println(s"[${context.sender()} --> $self] SayHiTo message")
          ref ! "Hi"
      }
    }

    val alice = actorSystem.actorOf(Props[SimpleActor], "alice")
    val bob = actorSystem.actorOf(Props[SimpleActor], "bob")

    case class SayHiTo(ref: ActorRef)

    // Actor.noSender --> alice --> bob(reply) --> alice
    alice ! SayHiTo(bob)
  }
}

결과

[Actor[akka://actorCapabilitiesDemo/deadLetters] --> Actor[akka://actorCapabilitiesDemo/user/alice#2067059408]] SayHiTo message
[Actor[akka://actorCapabilitiesDemo/user/alice#2067059408] --> Actor[akka://actorCapabilitiesDemo/user/bob#538084463]] String message: Hi
[Actor[akka://actorCapabilitiesDemo/user/bob#538084463] --> Actor[akka://actorCapabilitiesDemo/user/alice#2067059408]] Hello

 

4. dead letters (Actor.noSender)

import akka.actor.{Actor, ActorSystem, Props}

object TestActor {
  def main(args: Array[String]): Unit = {
    val actorSystem: ActorSystem = ActorSystem(name = "actorCapabilitiesDemo")

    class SimpleActor extends Actor {
      override def receive: Receive = { 
        case message: String =>
          println(s"[${context.sender()} --> $self] String message: $message")
      }
    }

    val alice = actorSystem.actorOf(Props[SimpleActor], "alice")
    alice ! "Hi" // who is a sender ?
  }
}

결과

[Actor[akka://actorCapabilitiesDemo/deadLetters] --> Actor[akka://actorCapabilitiesDemo/user/alice#906235305]] String message: Hi

 

5. forwarding messages

  • deadLetters -> A -> B
  • forwarding: sending a message with the ORIGINAL sender
object TestActor {
  def main(args: Array[String]): Unit = {
    val actorSystem: ActorSystem = ActorSystem(name = "actorCapabilitiesDemo")

    case class ForwardMessage(message: String, ref: ActorRef)

    class SimpleActor extends Actor {
      override def receive: Receive = {
        case message: String =>
          println(s"[${context.sender()} --> $self] String message: $message")

        case ForwardMessage(message, ref) =>
          println(s"[${context.sender()} --> $self] String message: $message")
          ref forward s"Forwards: $message"

      }
    }

    val A = actorSystem.actorOf(Props[SimpleActor], "A")
    val B = actorSystem.actorOf(Props[SimpleActor], "B")

    // Actor.noSender (ForwardMessage) -> A (Hi) -> B
    A ! ForwardMessage("Hi", B)
  }
}

 

결과

[Actor[akka://actorCapabilitiesDemo/deadLetters] --> Actor[akka://actorCapabilitiesDemo/user/A#130061245]] String message: Hi
[Actor[akka://actorCapabilitiesDemo/deadLetters] --> Actor[akka://actorCapabilitiesDemo/user/B#1295696439]] String message: Forwards: Hi

 

참고

아래처럼 forward 대신에 tell(!) 로 코드를 변경했을 때

// ref forward s"Forwards: $message"
ref ! s"Forwards: $message"

결과는 다음과 같이 ORIGINAL sender 대신에, 두번째 로그에서 바로 직전의 sender(/user/A) 가 로깅된다.

[Actor[akka://actorCapabilitiesDemo/deadLetters] --> Actor[akka://actorCapabilitiesDemo/user/A#1641879570]] String message: Hi
[Actor[akka://actorCapabilitiesDemo/user/A#1641879570] --> Actor[akka://actorCapabilitiesDemo/user/B#1749142701]] String message: Forwards: Hi