Actors完全是分层结构,任何你所创建的Actor必定是其他Actor的子Actor。让我们来分析一下:
假如我们用ActorSystem.actorOf方法创建了一个ActorRef,并尝试打印出它的路径
val actorSystem=ActorSystem("SupervisionActorSystem") val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor]) println (actorRef.path) // (prints) akka://SupervisionActorSystem/user/$a
正如你所看到的,输出的是一个路径,这和文件系统的文件路径很像。
1、akka前缀是固定格式的,因为这些是Akka Actors的路径-很像file://
或http://
前缀(虽然这里和协议没关系);
2、SupervisionActorSystem 仅仅是你所创建ActorSystem的名字;
3、我们将在下一节讨论user
4、$a是系统给你的Actor生成的名字。你是否会喜欢操作系统随便给你文件产生名字呢?你肯定会讨厌,因为以后你可能会引用这个名字,所以让我们给它取一个有意义的名字吧。
val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor], "teacherActor") println (actorRef.path) // (prints) akka://SupervisionActorSystem/user/teacherActor
好了,现在这个路径看起来有意义多了。
如果想及时了解Spark、Hadoop或者Hbase相关的文章,欢迎关注微信公共帐号:iteblog_hadoop
就像我们用ActorSystem创建出顶级的actor一样,我们可以用ActorContext创建出子actor。事实上,Actor强大的错误容错能力就依赖于Actor的分层结构,并且使得父Actor拥有管理子Actor生命的能力。
假如你有个TeacherSupervisor Actor,你想用它创建一个子Actor TeacherActor。你得使用ActorContext.actorOf方法,而不是 ActorSystem.actorOf:
class TeacherSupervisor extends Actor with ActorLogging { val teacherActor=context.actorOf(Props[TeacherActor], "teacherActor") ... ...
坦白地说,在任何应用程序中,你会创建大量的子Actor而非顶层的Actor-这也意味着你调用actorContext.actorOf的次数比actorSystem.actorOf要多的多。
如果想及时了解Spark、Hadoop或者Hbase相关的文章,欢迎关注微信公共帐号:iteblog_hadoop
你可能会注意到,子Actor的路径是akka://SupervisionActorSystem/user/teacherSupervisor/teacherActor,这跟你在父目录里面创建子目录看起来非常像。
当一项特定的任务由一个子任务或多个的子任务组成的时候,你会创建子Actor。亦或是有些任务在父Actor执行时候比较容易出现错误,这时候你想创建子Actor来隔离父Actor(这样即使它失败了,你可以恢复)。当你的Tasks之间没有父子关系的时候,你最好不要创建子Actor。
同样的,你无法阻止子Actor继续创建子Actor来委派自己的子任务。Actor以及创建Actor是非常廉价的,但它所带来的意义却非常深远(后面介绍到监督的时候会继续讲到这点)。
由于缺乏创意(For lack of a creativity),我们这里把ActorSystem比作Unix file system-带有一个/作为根目录,/etc, /usr, /bin和其他目录。
ActorSystem和这个很像。它会创建一些顶层Actors-包括了最重要的带有/路径的顶级Actor;带有/user路径的user Actor以及带有/system路径的System Actor(其中还有带有/deadLetters路径的DeadLetterActorRef,这在前面一篇文章中已经介绍了),
从代码角度看,ActorSystem内部由三个Actor组成(通过ActorRefProvider实现),他们是通过ActorSystem创建的Actor的根
1、systemGuardian actor �C 是所有属于/system目录下的根Actor
2、guardian actor �C 是所有属于/user目录下的根Actor
3、rootGuardian Actor -它是systemGuardian Actor和guardian Actor共同的根.
/** * Reference to the supervisor of guardian and systemGuardian; .... */ def rootGuardian: InternalActorRef /** * Reference to the supervisor used for all top-level user actors. */ def guardian: LocalActorRef /** * Reference to the supervisor used for all top-level system actors. */ def systemGuardian: LocalActorRef
/user (aka) user guardian
所有你用ActorSystem的actorOf方法创建的Actor都直接在/user目录下,比如StudentActor和TeacherActor。这也就是为什么在前面的teacherActor中包含/user/teacherActor路径
/system (aka) system guardian
当system guardian检测到userGuardian停掉了,它会关闭掉自己。这是因为如果userGuardian停掉了,它下面的所有业务Actor也会停掉,所以所有的监管Actor也应该停掉。
一共存在两种不同创建System Actors的方法,我这里说的Actor属于/system继承体系。
1、如前一篇文章所述,任何发送给已终止Actor的消息都会转发到一个叫DeadLetterActor的内部Actor的邮箱里。DeadLetterActor会将每条消息封装成一个DeadLetter消息并发布到EventStream中去。另一个叫DeadLetterListener的Actor会去消费这些DeadLetter消息并将它们作为日志打印出来。这里的DeadLetterListener就是一个System Actor,它的路径是 /system/deadLetterListener。
2、还记得我们前面的创建的TestEventListener?它也是System Actor。事实上,所有的akka.loggers都是作为System Actor来创建的。
class TeacherTest extends TestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]"""))) ... ...
/ (aka) root guardian
我们前面说了,/ Actor是 user guardians和system guardians的根.
从技术上来讲,根Actor也有父Actor,它只负责做一件事情,就是当根Actor崩溃的时候去关掉整个ActorSystem。严格来说,由于在Actor结构中并没有提到它,因此Akka团队把它叫作:
private[akka] val theOneWhoWalksTheBubblesOfSpaceTime: InternalActorRef = new MinimalActorRef { ...