2016-12-16 4 views
2

제가 배우와 자주 겪는 문제를 추상화하고 공통적 인 디자인 패턴을 통해이를 해결할 수있게하십시오. 다음과 같은 액터 계층 구조가 있다고 가정 해 보겠습니다.Akka 형제 재귀 ActorRef 요구 사항

── user 
    └── ServiceActor 
     ├── TicketManager 
     │   ├── WorkerA 
     │   ├── WorkerB 
     │   └── WorkerC 
     └── UserManager 
      ├── WorkerA 
      ├── WorkerB 
      └── WorkerC 

티켓 및 사용자 관리자는 중요하지 않습니다. TicketManager가 UserManager에 메시지를 보내고 그 반대의 경우도 마찬가지입니다. (이것들은 내가 사용 해본 예제 이름 일 뿐이며 실제 문제를 대표하지는 않습니다.)

다음 중 가장 적합한 것은 어느 것입니까?

옵션 1 :

TicketManager 생성자는 생성자 인수로 UserManager 걸립니다. UserManager 생성자는 TicketManager를 생성자 인수로 사용합니다. 그러나 둘 다 서로에 대해 재귀 호출을 할 수 없으므로 어쨌든 가능하지 않습니다.

옵션 2 :

UserManager이 actorSelection ("../ TicketManager")와 같은 actorSelection를 통해 TicketManager에 대한 참조를 만드는 등의 요구 사항에, 또는 한 번만 시작에 그것을 유지합니다.

옵션 3 : 특정 시점에서

는 UserManager/TicketManager는 다른 형제 자매에 대한 참조를 위해 부모 (ServiceActor를) 요청하고 부모가 참조하는 보유하고 적절하게 actorRef로 다시 대답한다.

옵션 4 :

이 상황을 결코 하는가? 형제가 어떤 방식 으로든 디자인을 "복잡하게"함에 따라 서로 이야기하는 것을 생각하지 않아야합니까? 나무 같은 계층 구조가 더 있어야합니까? 그러한 경우에 피할 수있는 일반적인 디자인 패턴은 무엇입니까?

요구 사항을 분명히했으면 좋겠어. 필요하면 추가로 명확히 할 수있게 해주시겠습니까?

+0

즉, 초기화 전술이 Edmondo에 의해 제안 통찰력 동안 것, 내가 옵션 4 옵션의 혼합물을하고 결국 3. 나의 배우 계층 구조의 엉망진창을 풀기위한 옵션 4. 그 외에도 형제 자매가 서로 이야기 할 필요가있는 시점에서 부모님을 통해 이야기했습니다. 나는 이것이 합리적인 패턴이라고 생각한다. – Ra41P

답변

2

옵션 4는 종속성이 복잡 해지면 아마도 가장 좋은 방법 일 것입니다.하지만 두 명의 액터의 경우 간단하게 메시지를 사용하여 액터를 초기화 할 수 있습니다. 액터는 가변 상태를 유지하도록 설계되었으므로 변경하면 문제가 없습니다.

그래서 나는 책임이있는 부모 배우, 당신은 당신이 ServiceDiscovery 어떤 종류의 것 찾고 있던 것 이상의 ActorSystem을 사용하고있는 경우

case class InitializeTicketManager(userManager:ActorRef) 

case class InitializeUserManager(ticketManager:ActorRef) 
val userManager = context.actorOf(...) 
val ticketManager = context.actorOf(...) 
userManager ! InitializeUserManager(ticketManager) 
ticketManager ! InitializeTicketManager(userManager) 
+0

답해 주셔서 감사합니다. 그러나 두 명 이상의 배우가 있다면 어떻게 될까요? 티켓, 사용자, 키오스크 등의 관리자가 최고 수준의 ServiceActor 바로 아래에있는 동일한 평면 수준에 있고, 각각 서로 깊은 하위 트리를 만들기 위해 충분한 하위 책임이없는 경우에는 어떻게됩니까? 여전히 init 메소드를 통해 초기화 할 것을 제안합니까? – Ra41P

1

을 다음 TickerManager 및 ActorManager이 할 수있는 만들 말할 것입니다. 또한 이것들이 도움이 될 것입니다 (Cluster Routing, Cluster Sharding, ClusterRouterGroup).

귀하의 것이 더 간단합니다. 단 하나의 ActorSystem 만 있습니다. 내가 게으른 놈들의 트위스트와 함께 옵션 1을 사용하고 통화별로 이름

package sumnulu 

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

/** 
    * Created by sumnulu 
    */ 
class RootActor extends Actor with ActorLogging { 
    lazy val serviceA: ActorRef = context.actorOf(ServiceA.props(serviceB)) 
    lazy val serviceB: ActorRef = context.actorOf(ServiceB.props(serviceA)) 

    override def preStart() = { 
    log info "Root Actor Ready" 
    serviceA ! "test" 
    } 

    override def receive = Actor.ignoringBehavior 
} 

class ServiceA(serviceB: ActorRef) extends Actor { 
    println("serviceA constructor") 
    serviceB ! "Hi" 


    override def receive: Receive = { 
    case x => println(s"self:$self from:$sender msg:$x") 
    } 
} 


class ServiceB(serviceA: ActorRef) extends Actor { 
    println("serviceB constructor") 
    serviceA ! "Hi" 

    override def receive: Receive = { 
    case x => println(s"self:$self from:$sender msg:$x") 
    } 

} 

//call-by-name important is i.e `=>` 
object ServiceA { 
    def props(serviceB: => ActorRef) = Props(new ServiceA(serviceB)) 
} 

//call-by-name important is i.e `=>` 
object ServiceB { 
    def props(serviceA: => ActorRef) = Props(new ServiceB(serviceA)) 
} 

object RootActor { 
    def props = Props[RootActor] 
} 

[info] [INFO] [02/18/2017 17:49:52.485] [Main-akka.actor.default-dispatcher-4] [akka://Main/user/app] Root Actor Ready 
[info] serviceA constructor 
[info] serviceB constructor 
[info] self:Actor[akka://Main/user/app/$a#1420483478] from:Actor[akka://Main/user/app#274353135] msg:test 
[info] self:Actor[akka://Main/user/app/$b#1573183992] from:Actor[akka://Main/user/app/$a#1420483478] msg:Hi 
[info] self:Actor[akka://Main/user/app/$a#1420483478] from:Actor[akka://Main/user/app/$b#1573183992] msg:Hi 
+0

와우! 이런 식으로 생각하지 마십시오. 가치에 의한 호출과 이름 별 호출의 차이를 이해하는 동안 왜 이것이 문제를 정확히 해결합니까? 그것에 대해 좀 자세히 설명해 주시겠습니까? – Ra41P