Akka Actor模型的简介与Actor的创建方式

Akka Actor其具有以下特点:

  • 系统中的所有事物都可以扮演一个Actor
  • Actor之间完全独立
  • 在收到消息时,Actor所采取的所有动作都是并行的,在一个方法中的动作没有明确的顺序
  • Actor有标识和当前行为描述
  • Actor可能被分成原始和非原始类别
  • 非原始Actor有一个由邮件地址表示的标识
  • 当前行为由一组知识(实例变量或本地状态)和定义Actor在收到消息时将采取的动作组成
  • 消息传递是非阻塞和异步的,其机制是邮件队列
  • 所有消息发送都是并行的。

绝大多数流行语言的并发是基于多线程之间的共享内存,使用同步方法防止写争夺,但Akka提供的则是基于Actor的并发模型。

Akka将复杂的Actor通信、Actor注册、Actor查找进行了封装。用户在写自己的Actor时,只需要实现akka.actor.Actor这个接口。

在Akka框架中,每一个Actor都有一个唯一的URL,该URL的定义格式和万维网地址的定义格式非常相似。

每一个Actor通过ActorSystem和Context初始化的时候,都会得到自己唯一的路径,路径格式如:akka.tcp://systemName@ip:port/user/topActorName/otherActorName,并且可以通过actorSelection(path)方法查找对应路径的Actor对象,该方法返回该Actor的引用。得到Actor后就可以发送消息了。

创建Actor

(1)通过实现akka.actor.Actor来创建Actor类
要定义自己的Actor类,需要继承Actor并实现receive方法。receive方法需要定义一系列case语句,来描述Actor能够处理哪些消息(使用标准的Scala模式匹配),以及实现对消息如何进行处理的代码。

import akka.actor._
object HelloScala {
  def main(args: Array[String]): Unit = {
    val _system = ActorSystem("HelloScalaNo7")
    // Props[MyActor]这里一定要写对
    val myActor = _system.actorOf(Props[MyActor],"iLoveMyActor")
    myActor ! "Hello My love"
  }
}
class MyActor extends Actor{
  override def receive: Receive = {
    case str: String => println(str)
    case _ =>
  }
}

Akka Actor receive消息循环是“无穷无尽的”。在receive方法中,提供一个对它能够接受消息的匹配规则,如果希望处理未知的消息,可以像上述代码中一样提供一个缺省的case分支,否则会有akka.actor.UnhandledMessage(message,sender,recipient)被发布到Actor系统(ActorSystem)的事件(EventStream)中。

上述代码中的actorOf的调用将返回一个实例的引用,这个引用是Actor访问句柄,可以用它来与实际的Actor进行交互。

ActorRef是不可变量,与它所代表的Actor之间是一对一的关系。ActorRef是可序列化的,并且它携带了网络信息。这意味着可以将它序列化以后,通过网络进行传送,在远程主机上它仍然代表原结点上的同一个Actor。

上述代码中,Actor是从系统创建的。也可以在其他的Actor中,使用Actor上下文(context)来创建,其中的区别在于监管树的组织方式。使用上下文时当前Actor将成为其创建子Actor的监管者。而使用系统创建的Actor将成为顶级Actor,它由系统(内部监管Actor)来监管。

(2)使用Actor的context创建Actor

import akka.actor._
object HelloScala {

  def main(args: Array[String]): Unit = {
    val _system = ActorSystem("HelloScala")
    // firstActor是使用系统创建的Actor,它将成为顶级Actor,它由系统(内部监管Actor)来监管。
    val firstActor = _system.actorOf(Props[FirstActor],"firstActor")
    firstActor ! "Hello MyActor!How are you?"
  }
}


class MyActor extends Actor{
  override def receive: Receive = {
    case str: String => println(s"test actor receive # ${str}")
    case _ =>
  }
}

class FirstActor extends  Actor{
  // context 方法创建Actor,使用上下文时当前Actor将成为其创建子Actor的监管者,所以FirstActor成为MyActor的监管者
  val myActor = context.actorOf(Props[MyActor],"myActor")

  // 输出监管目录
  println(s"MyActor's monitor#${myActor.path.parent.getElements.toString}")

  // Actor启动,preStart方法自动调用
  override def preStart(): Unit = println("FirstActor's preStart method was called!")

  override def receive: Receive = {
    case msg => myActor ! msg // 发给MyActor
    case _ =>
  }
}

(3)使用非缺省构造方法创建Actor

如果Actor的构造方法带参数,那么就不能使用actorOf(Props[TYPE])来创建。可以使用actorOf的非缺省构造方法,这样就可以用任意方式来创建Actor了。

import akka.actor._
object HelloScala {

  def main(args: Array[String]): Unit = {
    // 创建名为HelloAkka的ActorSystem
    val _system = ActorSystem("HelloAkka")
    // 非缺省构造方法创建,在Props中new 一个Test,并传入参数goat
    val testActor = _system.actorOf(Props(new Test("goat")),"testActor")
    testActor ! "Look at the stars!"
  }
}

class Test(name: String) extends Actor{
  // Actor启动,自动调用preStart方法
  override def preStart(): Unit = println("Test Actor preStart method was called!")
  override def receive: Receive = {
    // 匹配打印消息
    case str: String => println(s"actor's name:${name}#testActor receive msg:${str}")
    // 缺省处理
    case _ =>
  }
}

(4)创建匿名Actor
从某个Actor中派生新的Actor来完成特定的子任务时,可能使用匿名类来包含将要执行的代码会更方便。

import akka.actor._
object HelloScala {

  def main(args: Array[String]): Unit = {
    // 创建名为HelloAkka的ActorSystem
    val _system = ActorSystem("HelloAkka")
    // 使用ActorSystem的actorOf工厂方法创建Test4 Actor
    val test4Actor = _system.actorOf(Props[Test4],"test4Actor")
    test4Actor ! "Look at the stars!HO HO HO "
  }
}

class Test4 extends Actor{
  // Actor启动,自动调用preStart方法
  override def preStart(): Unit = println("Test4 Actor preStart method was called!")
  override def receive: Receive = {
    // 匹配打印消息
    case str: String =>
      // 使用context的actorOf方法创建Actor
      context.actorOf(Props(new Actor {
        // 创建匿名Actor
        override def receive: Receive = {
          case msg: String =>
            println(s"anonymous Actor receive message#${msg}")
            context.stop(self) // 停止匿名Actor
        }
      })).forward(str)// forward会把消息转发给刚刚定义的匿名Actor
    // 缺省处理
    case _ =>
  }
}

采用匿名Actor的方式,需要小心地避免捕捉外层Actor的引用,不要在匿名的Actor类中调用外层Actor的方法,这样会破坏Actor的封装,可能会引入同步bug和资源竞争,因为其他的Actor可能会与外层Actor同时进行调度。

猜你喜欢

转载自blog.csdn.net/weixin_40763897/article/details/93177065