Akka는 기본적으로 탑레벨 아키텍쳐의 설계방식으로 Actor를 위치시키고 접근을 하며
트리구조이지만, FullName을 알고있을시 직접 접근이 가능합니다. ( 접근의 유연성)
생성 관점에서 자식객체는 오로지 부모만 생성이 가능합니다.
장애 처리 관점에서 자식의 예외는 부모가 처리하는것을 1원칙으로, 부모의 예외를 자식노드에게 전가시키 지 않습니다.
이로써 상위 감독자에게 장애에대한 룰을 각각 적용할수가 있습니다. ( 장애를 허용하고 이를 유연하게 대처하는 장애 시스템 )
이러한 설계적 원칙이 리모트/클러스터 등으로 확장이 가능하여 스케일 아웃에 있어서 구조적 설계를 유연하게 합니다.
class PrintMyActorRefActor extends Actor { override def receive: Receive = { case "printit" => val secondRef = context.actorOf(Props.empty, "second-actor") //자식의 생성은 부모만 할수 있다. println(s"Second: $secondRef") } } val firstRef = system.actorOf(Props[PrintMyActorRefActor], "first-actor") println(s"First : $firstRef") firstRef ! "printit" //액터 생성 순서 first-actor <= second-actor 가 parent <= child 관계가 된다. First : Actor[akka://testSystem/user/first-actor#1053618476] Second: Actor[akka://testSystem/user/first-actor/second-actor#-1544706041]
아래와같이, rest의 endpoint 와 유사하게 접근이 가능합니다.
var parentActor= system.ActorSelection("akka://testSystem/user/first-actor"); var childActor= system.ActorSelection("akka://testSystem/user/first-actor/second-actor");
이런 구조적인 설계로, 부모의 액터를 정지시키면 자식의 액터를 모두 종류후 마지막에 부모 액터가 종료가됩니다.
이것은 부모가 자식을 책임지는 AKKA의 기본 예외 처리모델과도 부합합니다.
class StartStopActor1 extends Actor { override def preStart(): Unit = { println("first started") context.actorOf(Props[StartStopActor2], "second") } override def postStop(): Unit = println("first stopped") override def receive: Receive = { case "stop" => context.stop(self) } } class StartStopActor2 extends Actor { override def preStart(): Unit = println("second started") override def postStop(): Unit = println("second stopped") // Actor.emptyBehavior is a useful placeholder when we don't // want to handle any messages in the actor. override def receive: Receive = Actor.emptyBehavior } val first = system.actorOf(Props[StartStopActor1], "first") first ! "stop" //OutPut //first started //second started //second stopped //first stopped