Fire and Forget模式
这是发送消息的推荐方式,不会阻塞地等待消息。它拥有最好的并发性和可扩展性。
actor ! "hello"
//or
actor.tell("hello", actor)
!操作符实际上暗地里还做了一件事儿,就是将发送者自身作为隐式参数传递。在AKKA的实现如下所示:
abstract class ActorRef ... {
final def tell(msg: Any, sender: ActorRef): Unit = this.!(msg)(sender)
}
private[akka] class LocalActorRef private[akka]... {
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = actorCell.sendMessage(message, sender)
}
除非需要指定另外的ActorRef对象作为发送者,在Scala中,我们通常建议使用!操作符。
当要调用的actor不存在时,发送的消息会传递给dead letter actor。
通常情况下,我们会定义至少两个Actor来合作完成任务。一个Actor是任务的执行者,另一个Actor扮演任务的控制者。例如,我们需要一个LoadingActor去加载xml文件。此时,LoadingActor负责执行加载任务,至于何时执行该任务,执行完任务后应该怎么做,如何控制执行任务Actor的生命周期,都可以交给控制者。这个Actor必须定义为LoadingActor的父Actor,例如定义名为MasterActor。
sealed trait XmlMessage
case class SoftInfoMessage(fileName: String, fileNameIndex: Int, version: String) extends XmlMessage
case class FinishedMessage(fileName: String, index: Int)
class LoadingActor extends Actor {
def receive = {
case SoftInfoMessage(fileName, index, version) => {
loading(fileName, index, version)
sender ! new FinishMessage(fileName, index)
context.stop(self)
}
}
override def postStop() = {
println("stop")
super.postStop()
}
}
class MasterActor(numberOfWorker: Int) extends Actor {
def receive = {
case "loading" => {
(1 to numberOfWorker).foreach {
index => {
val loadingActor = context.actorOf(
Props[LoadingActor], name = "workerRouter%d".format(index))
loadingActor ! SoftInfoMessage("softinfo.xml", index, "4.13.10.01T09")
}
}
}
case FinishedMessage(fileName, index) => {
println("%s.%d is loaded.".format(fileName, index))
}
}
}
MasterActor创建了指定数量的LoadingActor,然后通过创建的子actor发送SoftInfoMessage消息。LoadingActor收到该消息后,会异步地执行加载任务。一旦加载任务执行完毕,就通过sender发送FinishedMessage消息给MasterActor,然后终止自己。MasterActor在收到FinishedMessage消息后,就知道任务执行完毕了。