actor dsl(领域特定语言)

简单的actor可以通过Act trait更方便的创建,下面的引入支持该功能

  1. import akka.actor.ActorDSL._
  2. import akka.actor.ActorSystem
  3. implicit val system = ActorSystem("demo")

假定上面的import在本章的所有例子中使用。隐式的actor系统服务作为ActorRefFactory在下面的所有例子中起作用。为了创建一个简单的actor,下面几行代码就足够了。

  1. val a = actor(new Act {
  2. become {
  3. case "hello" => sender() ! "hi" }
  4. })

这里,actor作为system.actorOf或者context.actorOf的角色,这依赖于它访问的是哪一个上下文。它持有一个隐式ActorRefFactory,它在actor内以implicit val context: ActorContext的形式被使用。在一个actor的外部,你必须声明一个隐式的ActorSystem或者你能明确的给出工厂方法。

分配一个context.become(添加或者替换新的行为)的两种方式分开提供用于为嵌套receives开启一个clutter-free notation。

  1. val a = actor(new Act {
  2. become { // this will replace the initial (empty) behavior
  3. case "info" => sender() ! "A"
  4. case "switch" =>
  5. becomeStacked { // this will stack upon the "A" behavior
  6. case "info" => sender() ! "B"
  7. case "switch" => unbecome() // return to the "A" behavior
  8. }
  9. case "lobotomize" => unbecome() // OH NOES: Actor.emptyBehavior
  10. }
  11. })

请注意,调用unbecome比调用becomeStacked更频繁的结果是原始的行为被装入了,这时Act trait是空的行为(在创建时外部的become仅仅替换它)。

生命周期管理

生命周期hooks也作为DSL元素被暴露出来,下面的方法调用将会替代相应hooks的内容。

  1. val a = actor(new Act {
  2. whenStarting { testActor ! "started" }
  3. whenStopping { testActor ! "stopped" }
  4. })

如果actor的逻辑的生命周期与重启周期(whenStopping在重启之前执行,whenStarting在重启之后执行)相匹配,上面的代码足够了。如果上面的代码不满足要求,可以用下面两个hooks。

  1. val a = actor(new Act {
  2. become {
  3. case "die" => throw new Exception
  4. }
  5. whenFailing { case m @ =>cause, msg) ) testActor ! m }
  6. whenRestarted { cause => testActor ! cause }
  7. })

也可以创建一个嵌套的actor,如孙子actor。

  1. // here we pass in the ActorRefFactory explicitly as an example
  2. val a = actor(system, "fred")(new Act {
  3. val b = actor("barney")(new Act {
  4. whenStarting { context.parent ! => "hello from " + self.path) }
  5. })
  6. become {
  7. case x => testActor ! x }
  8. })

注:在某些情况下,显式地传递ActorRefFactory到actor方法是必须的。(当编译器告诉你有歧义的隐式转换时你要注意)

孙子actor将被子actor监控,这个监控策略可以通过DSL的元素进行配置(监控指令也是Act trait的一部分)。

  1. superviseWith(OneForOneStrategy() {
  2. case e: Exception if e.getMessage == "hello" => Stop
  3. case _: Exception => Resume
  4. })

actor with stash

最后但是重要的是,有一些内置的方便的方法可以通过Stash trait发现静态的给定的actor子类型是否继承自RequiresMessageQueue trait (很难说new Act with Stash将不会工作,因为它的运行时擦除类型仅仅是一个匿名的actor子类型)。目的是自动的使用合适的基于队列的并满足stash的邮箱类型。如果你想用到这个功能,简单的继承ActWithStash即可。

  1. val a = actor(new ActWithStash {
  2. become {
  3. case 1 => stash()
  4. case 2 => testActor ! 2; unstashAll(); becomeStacked {
  5. case 1 => testActor ! 1; unbecome() }
  6. } })