actor锚定模式是指使用actorSelection对acor进行锚定的设计模式,也可以说是一个对actor的引用技巧。在某些情况下,我们可能需要能够根据Actor的path锚定对应的实例。简单来说就是,无论actor是因为异常导致的restart还是用户主动stop,然后再重新actorOf,只要actor的路径和name相同,我们都希望把消息发送给改Actor的一个实例。那我们来看一下actorSelection和ActorRef的使用区别。
class AnchorActor extends Actor{
override def preStart(): Unit = {
super.preStart()
println(s"self=$self,path=${self.path}")
}
override def receive: Receive = {
case any =>
println(s"Hello $any")
}
}
object AnchorPattern{
def main(args: Array[String]): Unit = {
val system = ActorSystem("AnchorPattern",ConfigFactory.load())
val anchorActor = system.actorOf(Props(new AnchorActor),"anchorActor")
anchorActor ! 1
val anchorActorSelection = system.actorSelection("/user/anchorActor")
anchorActorSelection ! 2
system.stop(anchorActor)
// 等待anchorActor彻底stop
Thread.sleep(3*1000)
system.actorOf(Props(new AnchorActor),"anchorActor")
anchorActor ! 3
anchorActorSelection ! 4 }
}
输出:
self=Actor[akka://AnchorPattern/user/anchorActor#-1941442858],path=akka://AnchorPattern/user/anchorActor
Hello 1
Hello 2
self=Actor[akka://AnchorPattern/user/anchorActor#489200584],path=akka://AnchorPattern/user/anchorActor
Hello 4
[INFO] [07/31/2018 16:59:48.102] [AnchorPattern-akka.actor.default-dispatcher-5] [akka://AnchorPattern/user/anchorActor] Message [java.lang.Integer] without sender to Actor[akka://AnchorPattern/user/anchorActor#-1941442858] was not delivered. [1] dead letters encountered. If this is not an expected behavior, then [Actor[akka://AnchorPattern/user/anchorActor#-1941442858]] may have terminated unexpectedly, This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
上面代码中,我们创建了一个AnchorActor实例,然后通过ActorRef和ActorSelection分别给AnchorActor发送了两条消息,anchorActor都收到了。之后我们把之前的AnchorActor实例给stop掉,再ActorRef和ActorSelection分别发送消息,由输出我们可以看出,第二次发消息时,只有anchorActorSelection发送成功了。这就证明了,ActorRef只能指向Actor的某个特定实例;而ActorSelection通过路径指向Actor的所有实例,即使该actor的实例被销毁然后重新创建,ActorSelection也能指向新的实例。
我这篇博客把ActorSelection发消息成为actor锚定模式,主要是想告诉读者ActorSelection与ActorRef的区别。那读者可能会问,既然ActorSelection可以对actor进行锚定,能指向最新的实例,为什么还要有ActorRef存在的必要呢?其实这要区别对待,结合场景来谈具体使用哪种形式。如果你的业务场景下,不区分Actor的实例,只要路径和name相同,就把所有的消息发送给它,那就是用ActorSelection;如果你的场景需要严格区分Actor的实例,比如不同的实例就代表不同的服务对象,不同服务对象的结果是不同的,此时就需要用ActorRef来通信。另外ActorSelection是无法保证对应路径的actor存在的,也就是说ActorSelection可能会把消息发送到deadLetters,而开发者是无法知道的;ActorRef一旦存在就可以发送消息,对方的Actor一定存在,当然消息也可能无法送达,比如对方的actor被stop掉或者网络故障。
虽然这个设计模式比较简单,但希望读者能够严格区分二者的关系,在合适的场景使用合适的技术。