actor dsl(领域特定语言)
简单的actor可以通过Act trait更方便的创建,下面的引入支持该功能
import akka.actor.ActorDSL._import akka.actor.ActorSystemimplicit val system = ActorSystem("demo")
假定上面的import在本章的所有例子中使用。隐式的actor系统服务作为ActorRefFactory在下面的所有例子中起作用。为了创建一个简单的actor,下面几行代码就足够了。
val a = actor(new Act {become {case "hello" => sender() ! "hi" }})
这里,actor作为system.actorOf或者context.actorOf的角色,这依赖于它访问的是哪一个上下文。它持有一个隐式ActorRefFactory,它在actor内以implicit val context: ActorContext的形式被使用。在一个actor的外部,你必须声明一个隐式的ActorSystem或者你能明确的给出工厂方法。
分配一个context.become(添加或者替换新的行为)的两种方式分开提供用于为嵌套receives开启一个clutter-free notation。
val a = actor(new Act {become { // this will replace the initial (empty) behaviorcase "info" => sender() ! "A"case "switch" =>becomeStacked { // this will stack upon the "A" behaviorcase "info" => sender() ! "B"case "switch" => unbecome() // return to the "A" behavior}case "lobotomize" => unbecome() // OH NOES: Actor.emptyBehavior}})
请注意,调用unbecome比调用becomeStacked更频繁的结果是原始的行为被装入了,这时Act trait是空的行为(在创建时外部的become仅仅替换它)。
生命周期管理
生命周期hooks也作为DSL元素被暴露出来,下面的方法调用将会替代相应hooks的内容。
val a = actor(new Act {whenStarting { testActor ! "started" }whenStopping { testActor ! "stopped" }})
如果actor的逻辑的生命周期与重启周期(whenStopping在重启之前执行,whenStarting在重启之后执行)相匹配,上面的代码足够了。如果上面的代码不满足要求,可以用下面两个hooks。
val a = actor(new Act {become {case "die" => throw new Exception}whenFailing { case m @ =>cause, msg) ) testActor ! m }whenRestarted { cause => testActor ! cause }})
也可以创建一个嵌套的actor,如孙子actor。
// here we pass in the ActorRefFactory explicitly as an exampleval a = actor(system, "fred")(new Act {val b = actor("barney")(new Act {whenStarting { context.parent ! => "hello from " + self.path) }})become {case x => testActor ! x }})
注:在某些情况下,显式地传递
ActorRefFactory到actor方法是必须的。(当编译器告诉你有歧义的隐式转换时你要注意)
孙子actor将被子actor监控,这个监控策略可以通过DSL的元素进行配置(监控指令也是Act trait的一部分)。
superviseWith(OneForOneStrategy() {case e: Exception if e.getMessage == "hello" => Stopcase _: Exception => Resume})
actor with stash
最后但是重要的是,有一些内置的方便的方法可以通过Stash trait发现静态的给定的actor子类型是否继承自RequiresMessageQueue trait (很难说new Act with Stash将不会工作,因为它的运行时擦除类型仅仅是一个匿名的actor子类型)。目的是自动的使用合适的基于队列的并满足stash的邮箱类型。如果你想用到这个功能,简单的继承ActWithStash即可。
val a = actor(new ActWithStash {become {case 1 => stash()case 2 => testActor ! 2; unstashAll(); becomeStacked {case 1 => testActor ! 1; unbecome() }} })
