请允许我抽象我经常与演员面对的问题,以及解决它的常见设计模式。假设我有以下actor层次结构:
── 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 :(得分:2)
如果依赖变得复杂,选项4可能是最好的方法,但是对于两个actor你可以简单地使用一个消息来初始化它们:actor被设计为保持可变状态,所以如果你改变它就没关系。
所以我会说负责创建TickerManager和ActorManager的父actor可以执行以下操作
case class InitializeTicketManager(userManager:ActorRef)
case class InitializeUserManager(ticketManager:ActorRef)
val userManager = context.actorOf(...)
val ticketManager = context.actorOf(...)
userManager ! InitializeUserManager(ticketManager)
ticketManager ! InitializeTicketManager(userManager)
答案 1 :(得分:1)
如果您使用多个ActorSystem,那么您所看到的将是某种ServiceDiscovery。这些也有帮助(Cluster Routing,Cluster Sharding,ClusterRouterGroup)。
您的工作更简单,只有一个ActorSystem。我会使用Option1来加入lazy vals和call-by-names,即
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