定义一个 Actor 类

要定义自己的Actor类,需要继承 Actor 并实现receive 方法.

receive 方法需要定义一系列 case 语句(类型为 PartialFunction[Any, Unit]) 来描述你的Actor能够处理哪些消息,以及如何进行处理。

如下例:

  1. import akka.actor.Actor
  2. import akka.actor.Props
  3. import akka.event.Logging
  4.  
  5. class MyActor extends Actor {
  6. val log = Logging(context.system, this)
  7. def receive = {
  8. case "test" log.info("received test")
  9. case _ log.info("received unknown message")
  10. }
  11. }

需要提供一个接受的所有消息的模式匹配规则,如果你希望处理未知的消息,你需要象上例一样提供一个缺省的case分支。否则会有一个 akka.actor.UnhandledMessage(message, sender, recipient) 被发布到 Actor系统(ActorSystem)‘的 事件流(EventStream)中。

 

发送消息

向actor发送消息是使用下列方法之一。

  • ! 意思是“fire-and-forget”, e.g. 异步发送一个消息并立即返回。也称为 tell.
  • ? 异步发送一条消息并返回一个 Future代表一个可能的回应。也称为 ask.

每一个消息发送者分别保证自己的消息的次序.

Tell: Fire-forget

这是发送消息的推荐方式。 不会阻塞地等待消息。它拥有最好的并发性和可扩展性。

  1. actor ! "hello"

如果是在一个Actor中调用 ,那么发送方的actor引用会被隐式地作为消息的 sender: ActorRef 成员一起发送. 目的actor可以使用它来向原actor发送回应, 使用 sender ! replyMsg.

如果 是从Actor实例发送的, sender成员缺省为 deadLetters actor 引用。

Ask: Send-And-Receive-Future

ask 模式既包含actor也包含future, 所以它是作为一种使用模式,而不是ActorRef的方法:

  1. import akka.pattern.{ ask, pipe }
  2.  
  3. case class Result(x: Int, s: String, d: Double)
  4. case object Request
  5.  
  6. implicit val timeout = Timeout(5 seconds) // 下面的 `?` 会用到
  7.  
  8. val f: Future[Result] =
  9. for {
  10. x ask(actorA, Request).mapTo[Int] // 直接调用
  11. s actorB ask Request mapTo manifest[String] // 隐式转换调用
  12. d actorC ? Request mapTo manifest[Double] // 通过符号名调用
  13. } yield Result(x, s, d)
  14.  
  15. f pipeTo actorD // .. 或 ..
  16. pipe(f) to actorD

上面的例子展示了将 ask 与 future上的 pipeTo 模式一起使用,因为这是一种非常常用的组合。 请注意上面所有的调用都是完全非阻塞和异步的: ask 产生 Future, 三个Future通过for-语法组合成一个新的Future,然后用 pipeTo 在future上安装一个 onComplete-处理器来完成将收集到的 Result 发送到其它actor的动作。

使用 ask 将会象 tell 一样发送消息给接收方, 接收方必须通过 sender ! reply 发送回应来为返回的 Future 填充数据. ask 操作包括创建一个内部actor来处理回应,必须为这个内部actor指定一个超时期限,过了超时期限内部actor将被销毁以防止内存泄露。

如果要以异常来填充future你需要发送一个 Failure 消息给发送方。这个操作不会在actor处理消息发生异常时自动完成。

  1. try {
  2. val result = operation()
  3. sender ! result
  4. } catch {
  5. case e: Exception
  6. sender ! akka.actor.Status.Failure(e)
  7. throw e
  8. }

如果一个actor没有完成future, 它会在超时时限到来时过期, 以 AskTimeoutException来结束. 超时的时限是按下面的顺序和位置来获取的:

  1. 显式指定超时:
  1. import akka.util.duration._
  2. import akka.pattern.ask
  3. val future = myActor.ask("hello")(5 seconds)
  1. 提供类型为 akka.util.Timeout的隐式参数, 例如,
  1. import akka.util.duration._
  2. import akka.util.Timeout
  3. import akka.pattern.ask
  4. implicit val timeout = Timeout(5 seconds)
  5. val future = myActor ? "hello"

参阅 Futures (Scala) 了解更多关于等待和查询future的信息。

FutureonComplete, onResult, 或 onTimeout 方法可以用来注册一个回调,以便在Future完成时得到通知。从而提供一种避免阻塞的方法。

警告

在使用future回调如 onComplete, onSuccess, and onFailure时, 在actor内部你要小心避免捕捉该actor的引用, i.e. 不要在回调中调用该actor的方法或访问其可变状态。这会破坏actor的封装,会引用同步bug和race condition, 因为回调会与此actor一同被并发调度。 不幸的是目前还没有一种编译时的方法能够探测到这种非法访问。 参阅: Actor与共享可变状态