监管
本节,我们将深入监管的细节。 我们还是用日志处理的实例来展示监管的各种策略。 本节我们将专注于/user actor下的supervisor层级。 这是所有应用程序的actor的生存空间。 在/user actor之上也有一个supervisor层级,我们在关闭系统流程中会介绍。 首先,我们介绍为应用程序建立supervisor层级的各种方法和各自的优缺点。 然后,介绍如何为不同的supervisor创建各自的监管策略。
Supervisor层级
supervisor层级就是一个actor通过一个函数创建另一个actor: 这个actor就成为子actor的supervisor。
supervisor层级决定了孩子的生命周期。 一旦一个actor被创建,它的整个生命周期都收父actor(supervisor)的监控,akka没有收养机制 (译者注:类似linux进程可以被init进程收养的机制)。 supervisor停止监控功能的唯一方法就是父actor终止子actor。 所有,从一开始就建立正确的监管层级是很重要的,特别是你不打算停止层级一部分并用另外的actor树来替换它。
最危险的actor(最可能崩溃的actor)应该尽可能的处于层级的底部。 低层的失败可以被它的supervisor处理或者上升到更高层的supervisor来处理。 当顶层的actor发生故障时,只能重启整个actor层级,甚至是关闭整个系统。
让我们看看日志处理程序的监管层级。 下图展示了,supervisor和actor之间的消息流程:从一个日志文件到数据库的一行记录。

在这个系统中,actor之间通过actorRef连接。 每个actor知道它要发送消息的actor的actorRef。 actorRef的存活状态依赖与引用的actor实例。 如果一个actor的实例停止了,这个actor的actorRef就指向了系统的deadLetters,deadLetters会中断应用程序。 supervisor中的所有actor都可以通过重启重复使用actorRef重复使用。
上图还显示了,fileWatcherSupervisor会创建fileWatcher和logProcessorsSupervisor。 fileWatcher会需要LogProcessorRef,但这是在logProcessorsSupervisor中创建的。 所有我们不能直接把LogProcessorRef传给fileWatcher,我们可以给logProcessorsSupervisor发消息请求LogProcessorRef, 然后再把LogProcessorRef发送给fileWatcher。 这种方式的好处是,actor直接直接进行交互,supervisor负责监督和创建。 缺点是,有问题就要重启actor,否则消息就发给deadLetters而丢失。 父supervisor使得去耦合很困难。 logProcessor必须由logProcessorsSupervisor创建,这就使得要把logProcessors的ActroRef传给fileWatcher很困难, 因为fileWatcher是由fileWatcherSupervisor创建的。
下图为不同的方法。 supervisor不只是创建和监督actors,他还负责转发发送给actors的消息。 从actor的角度看,转发是透明的,因为原始的发送者被保留了。 例如:logProcessor会认为是fileWatcher发送的,其实是logProcessorSupervisor转发的。

这种方式的好处是,supervisor提供一个间接层, supervisor可以停止它的孩子,然后重新启动另外一个,而其他的actor根本不知道这些。 相比前一种方法,这个中方法不会产生导致消息丢失的间隙。这种方式对父supervisor很方便,在创建actor时,可以直接使用子supervisor的引用。 例如:fileWatcherSupervisor在创建fileWatcher时可以直接使用logProcessingSupervisor的actorRef。 actor只管发送和接受消息,完全不会知道故障的发生。
这种转发的方式更灵活,更简单。下面是显示了如何构建一个这样的应用, 后面的章节我们重点介绍监管和策略的使用细节。
Listing 4.9 Build the supervisor hierarchy
    object LogProcessingApp extends App {
        val sources = Vector("file:///source1/", "file:///source2/")
        val system = ActorSystem("logprocessing")
        // create the props and dependencies
        val con = new DbCon("http://mydatabase")
        val writerProps = Props(new DbWriter(con))
        val dbSuperProps = Props(new DbSupervisor(writerProps))
        val logProcSuperProps = Props(
            new LogProcSupervisor(dbSuperProps))
        val topLevelProps = Props(new FileWatchingSupervisor(
            sources,
            logProcSuperProps))
        system.actorOf(topLevelProps)
    }
上面显示了日志处理程序是怎么建立的。 Props对象被当做参数传给上一层的supervisor,直到最顶层,最终由system.actorFor来创建顶层的supervisor,其他的actor就会一路创建下来。 后面的章节,我们看看actor如何创建自己的孩子,实际上它只需要用接受的Props调用context.actorOf(props)就可以了。
现在我们已经知道如何构建监管层级了,下面的章节,我看看都有那些监管策略可用。