Akka框架学习

Akka框架学习

转载自Spark修炼之道

一、并发编程简介

多核处理器的出现使并发编程(Concurrent Programming)成为开发人员必备的一项技能,许多现代编程语言都致力于解决并发编程问题。并发编程虽然能够提高程序的性能,但传统并发编程的共享内存通信机制对开发人员的编程技能要求很高,需要开发人员通过自身的专业编程技能去避免死锁、互斥等待及竞争条件(Race Condition)等,熟悉Java语言并发编程的读者们对这些问题的理解会比较深刻,这些问题使得并发编程比顺序编程要困难得多。

Scala语言并没有直接使用Java语言提供的并发编程库,而是通过Actor模型来解决Java并发编程中遇到的各种问题,为并发编程提供了更高级的抽象。

1、重要概念

(1)并发和并行

并发和并行从宏观来看,都是为进行多任务运行,但并发(Concurrency)和并行(parallelism)两者之间是有区别的。

  • 并行是指两个或者两个以上任务在同一时刻同时运行
  • 并发是指两个或两个以上的任务在同一时间段内运行,即一个时间段中有几个任务都处于已启动运行到运行完毕之间,这若干任务在同一CPU上运行但任一个时刻点上只有一个任务运行。

这里写图片描述
图1 进程、线程模型

图1给出了多核处理器下的现代操作系统进程和线程模型,图中进程2的线程1被调用度到处理器的核2上运行、进程3的线程1被调度到处理器的核3上运行,进程2的线程1和进程3的线程1是并行的,它们可以同时运行,而进程1的线程1和线程2都调度到处理器的核1上运行,此外它们还共享线程1的内存空间,在运行时面临着资源竞争包括CPU、内存及其它如IO等,它们在同一时候只能运行一个,但在一段时间内都可以运行,因此进程1的线程1和线程2是并发执行的。

(2)横向扩展和纵向扩展
  • 纵向扩展(Scale Up)指的是增加程序的进度或线程数量,提高程序的并发性;
  • 横向扩展(Scale Out)指的是程序可以扩展到其它机器上运行,即通过分布式系统来提到程序的并行度。

传统的Java并发编程模型不容易进行纵向扩展,因此并发的线程数越多,程序行为便会变得很难理解和控制,更多的线程加入到资源竞争,出现死锁等情况的概率增加。横向扩展比纵向扩展困难更大,此时的程序变为分布式环境下的应用,情况更为复杂,对开发人员的要求更高。Scala提供的Actor模型可以解决并发应用程序的横向扩展和纵向扩展问题,如图2、图3给出了基本Actor模型的横向扩展和纵向扩展。

这里写图片描述
图2 纵向扩展

这里写图片描述
图3 横向扩展

2、Actor模型

在使用Java语言进行并发编程时,需要特别关注共享的数据结构,线程间的资源竞争容易导致死锁等问题,而Actor模型便是要解决线程和锁带来的问题。

Actor是一种基于事件(Event-Based)的轻量级线程,在使用Actor进行并发编程时只需要关注代码结构,而不需要过分关注数据结构,因此Actor最大限度地减少了数据的共享。

Actor由三个重要部分组成,它们是状态(state),行为(Behavior)和邮箱(Mailbox)。Actor与Actor之间的交互通过消息发送来完成,Actor模型如图4所示,状态指的是Actor对象的变量信息,它可以是Actor对象中的局部变量、占用的机器资源等,状态只会根据Actor接受的消息而改变,从而避免并发环境下的死锁等问题;行为指的是Actor的计算行为逻辑,它通过处理Actor接收的消息而改变Actor状态;邮箱(mailbox)建立起Actor间的连接,即Actor发送消息后,另外一个Actor将接收的消息放入到邮箱中待后期处理,邮箱的内部实现是通过队列来实现的,队列可以是有界的(Bounded)也可以是无界的(Unbounded),有界队列实现的邮箱容量固定,无界队列实现的邮箱容易不受限制。

这里写图片描述
图4 Actor模型

不难看出,Actor模型是对现实世界的高度抽象,它具有如下特点:

  1. Actor之间使用消息传递机制进行通信,传递的消息使用的是不可变消息;
  2. Actor之间并不共享数据结构,如果有数据共享则通过消息发送的方式进行;
  3. 各Actor都有对应的mailbox,如果其它Actor向该Actor发送消息,消息将入队待后期处理;
  4. Actor间的消息传递通过异步的方式进行,即消息的发送者发送完消息后不必等待回应便可以返回继承处理其它任务。

3、Akka并发编程框架

Scala语言中原生地支持Actor模型,只不过功能还不够强大,从Scala 2.10版本之后,Akka框架成为Scala包的一部分,可以在程序中直接使用。Akka框架是一个以Actor模型为基础构建的基于事件的并发编程框架,底层使用Scala语言实现,提供Java和Scala两种API,它属于LightBend公司(原Typesafe公司)体系结构的一部分,如图5所示。

这里写图片描述
图5 Lightbend 体系结构

Akka框架意在简化高并发、可扩展及分布式应用程序的设计,它具有如下优势:

  1. 使用Akka框架编写的应用程序既可以横向扩展(Scale Out)、也可纵向扩展(Scale Up)。
  2. 编写并发应用程序更简单,Akka提供了更高的抽象,开发人员只需要专注于业务逻辑,而无需像Java语言那样需要处理底级语义如线程、锁及非阻塞IO等。
  3. 高容错,Akka使用“let it crashes”机制,当Actor出错时可以快速恢复。
  4. 事件驱动的架构,Akka中的Actor之间的通信采用异步消息发送,能够完美支持事件驱动。
  5. 位置透明,无论是Actor运行在本地机器还是远程机器上,对于用户来说都是透明的,这极大地简化了多核处理器和分布式系统上的应用程序编程。
  6. 事务支持能力,支持软件事务内存(software transactional memory,STM),使Actor具有原子消息流的操作能力。

Akka框架由下列十个组件构成:

  1. akka-actor :包括经典的Actor、Typed Actors、IO Actor等
  2. akka-remote:远程Actor
  3. akka-testkit:测试Actor系统的工具箱
  4. akka-kernel :Akka微内核,用于运行精简的微型应用程序服务器,无需运行于Java应用服务器上。
  5. akka-transactor :Transactors 即支持事务的 actors,集成了Scala STM
  6. akka-agent – 代理, 同样集成了Scala STM
  7. akka-camel – 集成Apache Camel
  8. akka-zeromq – 集成ZeroMQ 消息队列
  9. akka-slf4j – 支持SLF4J 日志功能
  10. akka-filebased-mailbox – 支持基于文件的mailbox

二、Actor模型

1. 定义Actor

通过扩展akka.actor.Actor 特质并实现receive方法来定义Actor,代码示例如下

//通过扩展Actor并实现receive方法来定义Actor
class MyActor extends Actor {
    //获取LoggingAdapter,用于日志输出
    val log = Logging(context.system, this)

    //实现receive方法,定义Actor的行为逻辑,返回的是一个偏函数
    def receive = {
      case "test" => log.info("received test")
      case _      => log.info("received unknown message")
    }
  }

receive方法被定义在Actor当中,方法标签如下

//Actor中的receive方法定义,
type Receive = PartialFunction[Any, Unit]
def receive: Actor.Receive

下面给出其完整使用代码:

object Example_01 extends App{
  import akka.actor.Actor
  import akka.event.Logging
  import akka.actor.ActorSystem
  import akka.actor.Props

  class MyActor extends Actor {
    val log = Logging(context.system, this)

    def receive = {
      case "test" => log.info("received test")
      case _      => log.info("received unknown message")
    }
  }
 //创建ActorSystem对象
  val system = ActorSystem("MyActorSystem")
  //返回ActorSystem的LoggingAdpater
  val systemLog=system.log
  //创建MyActor,指定actor名称为myactor
  val myactor = system.actorOf(Props[MyActor], name = "myactor")

  systemLog.info("准备向myactor发送消息")
  //向myactor发送消息
  myactor!"test"
  myactor! 123

  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码运行结果:

[INFO] [04/02/2016 09:29:54.223] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 09:29:54.224] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received test
[INFO] [04/02/2016 09:29:54.224] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received unknown message

输出“[INFO] [04/02/2016 09:29:54.224] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received test”中的[MyActorSystem-akka.actor.default-dispatcher-3]为对应的线程名,[akka://MyActorSystem/user/myactor]为Actor路径信息, received test为

def receive = {
      case "test" => log.info("received test")
      case _      => log.info("received unknown message")
    }

方法处理后的输出。关于[akka://MyActorSystem/user/myactor]路径信息,将在后续内容中进行详细阐述。


也可以通过混入ActorLogging来实现日志功能,具体代码如下:

class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test")
      case _      => log.info("received unknown message")
    }
  }

ActorLogging的定义如下:

trait ActorLogging { this: Actor =>
  private var _log: LoggingAdapter = _

  def log: LoggingAdapter = {
    // only used in Actor, i.e. thread safe
    if (_log eq null)
      _log = akka.event.Logging(context.system, this)
    _log
  }

}

完整代码如下:

/*
 *定义Actor时混入ActorLogging
 */
object Example_02 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test")
      case _      => log.info("received unknown message")
    }
  }

  //创建ActorSystem对象
  val system = ActorSystem("MyActorSystem")
  //返回ActorSystem的LoggingAdpater
  val systemLog=system.log
  //创建MyActor,指定actor名称为myactor
  val myactor = system.actorOf(Props[MyActor], name = "myactor")

  systemLog.info("准备向myactor发送消息")
  //向myactor发送消息
  myactor!"test"
  myactor! 123

  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码运行结果:

[INFO] [04/02/2016 09:39:21.088] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 09:39:21.089] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received test
[INFO] [04/02/2016 09:39:21.089] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received unknown message

代码原理与Example_01类似,这里不再赘述。

2. 创建Actor

在前面两个例子中,通过

val myactor = system.actorOf(Props[MyActor], name = "myactor")

创建Actor,需要注意的是system.actorOf方法返回的是ActorRef对象,ActorRef为Actor的引用,使用ActorRef对象可以进行消息的发送等操作。Props为配置对象,在创建Actor时使用,它是不可变的对象,因此它是线程案例且完全可共享的。Akka中创建Actor时,也允许直接传入MyActor对象的引用,例如

//直接通过new MyActor的方式传入MyActor对象的引用,注意这里是Props(new MyActor)
val myactor = system.actorOf(Props(new MyActor), name = "myactor")

但是Akka不推荐这么做,官方文档给出的解释是这种方式会导致不可序列化的Props对象且可能会导致竞争条件(破坏Actor的封装性)。另外需要特别注意的是,不允许通过下列代码创建Actor

//下列两行代码编译可以通过,但运行时出抛出异常
  val  myActor=new MyActor
  val myactor = system.actorOf(Props(myActor), name = "myactor")

完整运行代码如下:

/*
 *创建Actor
 */
object Example_03 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test")
      case _      => log.info("received unknown message")
    }
  }

  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //下列两行代码编译可以通过,但运行时出抛出异常
  val  myActor=new MyActor
  val myactor = system.actorOf(Props(myActor), name = "myactor")

  systemLog.info("准备向myactor发送消息")
  //向myactor发送消息
  myactor!"test"
  myactor! 123

  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

运行结果如下:

Exception in thread "main" akka.actor.ActorInitializationException: You cannot create an instance of [chapter02.Example_03$MyActor] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.
    at akka.actor.ActorInitializationException$.apply(Actor.scala:167)
    at akka.actor.Actor$class.$init$(Actor.scala:423)
    at chapter02.Example_03$MyActor.<init>(MyActor.scala:73)
    at chapter02.Example_03$delayedInit$body.apply(MyActor.scala:84)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
	at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.collection.immutable.List.foreach(List.scala:318)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
    at scala.App$class.main(App.scala:71)
    at chapter02.Example_03$.main(MyActor.scala:68)
    at chapter02.Example_03.main(MyActor.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

从“You cannot create an instance of [chapter02.Example_03$MyActor] explicitly using the constructor (new). You have to use one of the ‘actorOf’ factory methods to create a new actor.”可以看到,不能通过显式地调用构造函数创建Actor,只能使用actorOf工厂方法创建Actor。

下面介绍2种在实际中经常使用的Actor创建方法

(1)调用system.actorOf创建Actor

val system = ActorSystem("mySystem")
val myActor = system.actorOf(Props[MyActor], "myactor2")

完整代码在Example_01、Example_02中已经演示过了,这里需要说明的是通过system.actorOf工厂方法创建的Actor为顶级Actor

这里写图片描述

在Akka框架中,每个Akka应用程序都会有一个守卫Actor,名称为user,所有通过system.actorOf工厂方法创建的Actor都为user的子Actor,也是整个Akka程序的顶级Actor。

(2)调用context.actorOf创建Actor

完整代码如下:

/*
 *创建Actor,调用context.actorOf方法
 */
object Example_04 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class FirstActor extends Actor with ActorLogging{
    //通过context.actorOf方法创建Actor
    val child = context.actorOf(Props[MyActor], name = "myChild")
    def receive = {
      case x => child ! x;log.info("received "+x)
    }

  }

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test")
      case _      => log.info("received unknown message")
    }
  }

  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val myactor = system.actorOf(Props[FirstActor], name = "firstActor")

  systemLog.info("准备向myactor发送消息")
  //向myactor发送消息
  myactor!"test"
  myactor! 123
  Thread.sleep(5000)
  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码运行结果

[INFO] [04/02/2016 15:05:34.770] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor/myChild] received test
[INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor/myChild] received unknown message

通过代码的运行结果可以看到,FirstActor的Actor路径信息为akka://MyActorSystem/user/firstActor,而通过

class FirstActor extends Actor with ActorLogging{
    //通过context.actorOf方法创建Actor
    val child = context.actorOf(Props[MyActor], name = "myChild")
    def receive = {
      case x => child ! x;log.info("received "+x)
    }

  }

代码使用context.actorOf创建的MyActor,其Actor路径信息为[akka://MyActorSystem/user/firstActor/myChild],这意味着mychild为firstActor的子Actor,层次结构如下图所示

这里写图片描述

也就是说context.actorOf和system.actorOf的差别是system.actorOf创建的actor为顶级Actor,而context.actorOf方法创建的actor为调用该方法的Actor的子Actor

3. Actor API解析

Actor中的主要成员变量和方法定义如下:

package akka.actor
trait Actor extends scala.AnyRef {
  type Receive = akka.actor.Actor.Receive

  //context变量暴露当前Actor的上下文信息及当前消息
  implicit val context : akka.actor.ActorContext = { /* compiled code */ }

  //self作为当前ActorRef的引用
  implicit final val self : akka.actor.ActorRef = { /* compiled code */ }

  //当前Actor接收到最后一条消息对应的消息发送者(Actor)
  final def sender() : akka.actor.ActorRef = { /* compiled code */ }

  //receive方法,抽象方法,定义Actor的行为逻辑
  def receive : akka.actor.Actor.Receive

  //内部使用API 
  protected[akka] def aroundReceive(receive : akka.actor.Actor.Receive, msg : scala.Any) : scala.Unit = { /* compiled code */ }
  protected[akka] def aroundPreStart() : scala.Unit = { /* compiled code */ }
  protected[akka] def aroundPostStop() : scala.Unit = { /* compiled code */ }
  protected[akka] def aroundPreRestart(reason : scala.Throwable, message : scala.Option[scala.Any]) : scala.Unit = { /* compiled code */ }
  protected[akka] def aroundPostRestart(reason : scala.Throwable) : scala.Unit = { /* compiled code */ }

   //监督策略,用于Actor容错处理
  def supervisorStrategy : akka.actor.SupervisorStrategy = { /* compiled code */ }

  //Hook方法,用于Actor生命周期监控 
  @scala.throws[T](classOf[scala.Exception])
  def preStart() : scala.Unit = { /* compiled code */ }
  @scala.throws[T](classOf[scala.Exception])
  def postStop() : scala.Unit = { /* compiled code */ }
  @scala.throws[T](classOf[scala.Exception])
  def preRestart(reason : scala.Throwable, message : scala.Option[scala.Any]) : scala.Unit = { /* compiled code */ }
  @scala.throws[T](classOf[scala.Exception])
  def postRestart(reason : scala.Throwable) : scala.Unit = { /* compiled code */ }

  //发送给Actor的消息,Actor没有定义相应的处理逻辑时,会调用此方法
  def unhandled(message : scala.Any) : scala.Unit = { /* compiled code */ }
}
object Actor extends scala.AnyRef {
  type Receive = scala.PartialFunction[scala.Any, scala.Unit]

  //空的行为逻辑
  @scala.SerialVersionUID(1)
  object emptyBehavior extends scala.AnyRef with akka.actor.Actor.Receive {
    def isDefinedAt(x : scala.Any) : scala.Boolean = { /* compiled code */ }
    def apply(x : scala.Any) : scala.Nothing = { /* compiled code */ }
  }
  //Sender为null
  @scala.SerialVersionUID(1)
  final val noSender : akka.actor.ActorRef = { /* compiled code */ }
}
(1)Hook方法,preStart()、postStop()方法的使用
/*
 *Actor API: Hook方法
 */
  object Example_05 extends App{
    import akka.actor.Actor
    import akka.actor.ActorSystem
    import akka.actor.Props

    class FirstActor extends Actor with ActorLogging{
      //通过context.actorOf方法创建Actor
      var child:ActorRef = _

      //Hook方法,preStart(),Actor启动之前调用,用于完成初始化工作
      override def preStart(): Unit ={
        log.info("preStart() in FirstActor")
        //通过context上下文创建Actor
        child = context.actorOf(Props[MyActor], name = "myChild")
      }
      def receive = {
        //向MyActor发送消息
        case x => child ! x;log.info("received "+x)
      }

      //Hook方法,postStop(),Actor停止之后调用
      override def postStop(): Unit = {
        log.info("postStop() in FirstActor")
       }
    }

    class MyActor extends Actor with ActorLogging{
      //Hook方法,preStart(),Actor启动之前调用,用于完成初始化工作
      override def preStart(): Unit ={
        log.info("preStart() in MyActor")
      }
      def receive = {
        case "test" => log.info("received test")
        case _      => log.info("received unknown message")
      }

      //Hook方法,postStop(),Actor停止之后调用
      override def postStop(): Unit = {
        log.info("postStop() in MyActor")
      }
    }

    val system = ActorSystem("MyActorSystem")
    val systemLog=system.log

    //创建FirstActor对象
    val myactor = system.actorOf(Props[FirstActor], name = "firstActor")

    systemLog.info("准备向myactor发送消息")
    //向myactor发送消息
    myactor!"test"
    myactor! 123
    Thread.sleep(5000)
    //关闭ActorSystem,停止程序的运行
    system.shutdown()
  }

代码运行结果:

[INFO] [04/02/2016 17:04:49.607] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 17:04:49.607] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] preStart() in FirstActor
[INFO] [04/02/2016 17:04:49.607] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 17:04:49.607] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 17:04:49.608] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor/myChild] preStart() in MyActor
[INFO] [04/02/2016 17:04:49.608] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor/myChild] received test
[INFO] [04/02/2016 17:04:49.608] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor/myChild] received unknown message
[INFO] [04/02/2016 17:04:54.616] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myChild] postStop() in MyActor
[INFO] [04/02/2016 17:04:54.617] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] postStop() in FirstActor

在代码中分别对postStop、preStart方法进行了重写,在preStart方法中通过代码

child = context.actorOf(Props[MyActor], name = "myChild")

对成员变量child进行初始化,然后在postStop方法中使用

//通过context上下文停止MyActor的运行
        context.stop(child)

停止MyActor的运行。在使用代码

//创建FirstActor对象
val myactor = system.actorOf(Props[FirstActor], name = "firstActor")

创建FirstActor时,便会调用preStart方法完成MyActor的创建,因此首先会执行FirstActor中的preStart()方法

dispatcher-4] [akka://MyActorSystem/user/firstActor] preStart() in FirstActor

然后在创建MyActor时执行MyActor中定义的preStart方法

[INFO] [04/02/2016 17:04:49.608] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor/myChild] preStart() in MyActor

在执行

//关闭ActorSystem,停止程序的运行
    system.shutdown()

FirstActor作为MyActor的Supervisor,会先停止MyActor,再停止自身,因此先调用MyActor的postStop方法,再调用FirstActor的postStop方法。

(2)成员变量self及成员方法sender方法的使用

整体代码如下:

/*
 *Actor API:成员变量self及sender()方法的使用
 */
  object Example_05 extends App{
    import akka.actor.Actor
    import akka.actor.ActorSystem
    import akka.actor.Props

    class FirstActor extends Actor with ActorLogging{
      //通过context.actorOf方法创建Actor
      var child:ActorRef = _

      override def preStart(): Unit ={
        log.info("preStart() in FirstActor")
        //通过context上下文创建Actor
        child = context.actorOf(Props[MyActor], name = "myActor")
      }
      def receive = {
        //向MyActor发送消息
        case x => child ! x;log.info("received "+x)
      }

    }

    class MyActor extends Actor with ActorLogging{
      self!"message from self reference"
      def receive = {
        case "test" => log.info("received test");sender()!"message from MyActor"
        case "message from self reference"=>log.info("message from self refrence")
        case _      => log.info("received unknown message");
      }

    }

    val system = ActorSystem("MyActorSystem")
    val systemLog=system.log

    //创建FirstActor对象
    val myactor = system.actorOf(Props[FirstActor], name = "firstActor")

    systemLog.info("准备向myactor发送消息")
    //向myactor发送消息
    myactor!"test"
    myactor! 123
    Thread.sleep(5000)
    //关闭ActorSystem,停止程序的运行
    system.shutdown()
  }

运行结果:

[INFO] [04/02/2016 18:40:37.805] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 18:40:37.805] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] preStart() in FirstActor
[INFO] [04/02/2016 18:40:37.806] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 18:40:37.806] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 18:40:37.806] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received test
[INFO] [04/02/2016 18:40:37.806] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] message from self refrence
[INFO] [04/02/2016 18:40:37.806] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] received message from MyActor
[INFO] [04/02/2016 18:40:37.806] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received unknown message
[INFO] [04/02/2016 18:40:37.806] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received unknown message

代码中使用

self!"message from self reference"

向自身发送了一条消息,receive方法通过

case "message from self reference"=>log.info("message from self refrence")

对这条消息进行处理。receive方法在处理

def receive = {
        case "test" => log.info("received test");sender()!"message from MyActor"

“test”消息时,会调用

sender()!"message from MyActor"

向sender(本例中为FirstActor)发送”message from MyActor”消息,FirstActor使用

def receive = {
        //向MyActor发送消息
        case x => child ! x;log.info("received "+x)
      }

处理消息时又向MyActor回送该消息,因此最终的输出有两个unknown message,分别对应123和”message from MyActor”

(3) unhandled方法的使用

unhandled方法用于处理没有被receive方法处理的消息,下面的代码给出的是当不重写unhandled方法时的代码

/*
*Actor API:unhandled方法
*/
object Example_06 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props
  class FirstActor extends Actor with ActorLogging{
    def receive = {
      //向MyActor发送消息
      case "test" => log.info("received test")
    }

  }
  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val myactor = system.actorOf(Props[FirstActor], name = "firstActor")

  systemLog.info("准备向myactor发送消息")
  //向myactor发送消息
  myactor!"test"
  myactor! 123
  Thread.sleep(5000)
  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码输出:

[INFO] [04/02/2016 19:14:11.992] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 19:14:11.992] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] received test

不难看出,对于

myactor! 123

发送的这条消息没有被处理,没有任何的处理逻辑。在实际开发过程中,可能会对不能被处理的消息增加一些应对逻辑,此时可以重写unhandled方法,代码如下:

/*
*Actor API:unhandled方法的使用
*/
object Example_06 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class FirstActor extends Actor with ActorLogging{
    def receive = {
      //向MyActor发送消息
      case "test" => log.info("received test")
    }

    //重写unhandled方法
    override def unhandled(message: Any): Unit = {
      log.info("unhandled message is {}",message)
    }
  }

  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val myactor = system.actorOf(Props[FirstActor], name = "firstActor")

  systemLog.info("准备向myactor发送消息")
  //向myactor发送消息
  myactor!"test"
  myactor! 123
  Thread.sleep(5000)
  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码输出结果:

[INFO] [04/02/2016 19:17:18.458] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 19:17:18.458] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 19:17:18.458] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] unhandled message is 123

其它如preRestart等方法的使用将在Akka容错部分进行讲解。

4. Actor引用、Actor路径

下图是Akka官方文档中给出的一张图

这里写图片描述

该图清晰地说明了ActorPath,ActorRef,Actor及ActorSystem之间的关系,并说明了Actor整体的层次结构。前面我们提到,Akka应用程序会持有一个名称为user的Actor,该Actor被称为guardian supervisor(守卫监督器),无论是ActorSystem创建的Actor还是通过ActorContext创建的Actor都为user的子类,它是最顶级的Actor。

(一)ActorRef

对于ActorRef,我们已经很熟悉了,通过调用ActorSystem.actorOf方法可以创建Actor,返回的便是ActorRef,例如代码

//创建FirstActor对象
  val myactor = system.actorOf(Props[FirstActor], name = "firstActor")

返回的便是FirstActor的ActorRef对象,ActorRef最重要的作用便是向Actor发送消息,例如

//向myactor发送消息
myactor!"test"
myactor! 123

另外,还可以通过context隐式对象获取父Actor和子Actor的ActorRef,示例代码如下:

/*
*Actor API:成员变量self及sender()方法的使用
*/
object Example_07 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class FirstActor extends Actor with ActorLogging{
    //通过context.actorOf方法创建Actor
    var child:ActorRef = _
    override def preStart(): Unit ={
      log.info("preStart() in FirstActor")
      //通过context上下文创建Actor
      child = context.actorOf(Props[MyActor], name = "myActor")
    }
    def receive = {
      //向MyActor发送消息
      case x => child ! x;log.info("received "+x)
    }
  }

  class MyActor extends Actor with ActorLogging{
    var parentActorRef:ActorRef=_
    override def preStart(): Unit ={
      //通过context.parent获取其父Actor的ActorRef
      parentActorRef=context.parent
    }
    def receive = {
      case "test" => log.info("received test");parentActorRef!"message from ParentActorRef"
      case _      => log.info("received unknown message");
    }

  }
  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val myactor = system.actorOf(Props[FirstActor], name = "firstActor")
  //获取ActorPath
  val myActorPath=system.child("firstActor")
  //通过system.actorSelection方法获取ActorRef
  val myActor1=system.actorSelection(myActorPath)
  systemLog.info("准备向myactor发送消息")
  //向myActor1发送消息
  myActor1!"test"
  myActor1! 123
  Thread.sleep(5000)
  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码运行结果

[INFO] [04/02/2016 20:28:08.941] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] preStart() in FirstActor
[INFO] [04/02/2016 20:28:08.942] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 20:28:08.943] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 20:28:08.943] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 20:28:08.943] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received test
[INFO] [04/02/2016 20:28:08.943] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received unknown message
[INFO] [04/02/2016 20:28:08.943] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor] received message from ParentActorRef
[INFO] [04/02/2016 20:28:08.943] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received unknown message

代码中,使用

//通过context.parent获取其父Actor的ActorRef
      parentActorRef=context.parent

获取MyActor 的直接父Actor的ActorRef,在本例中为FirstActor,因为在FirstActor中,我们使用

//通过context上下文创建Actor
      child = context.actorOf(Props[MyActor], name = "myActor")

创建了MyActor,FirstActor便自动成为MyActor的直接Supervisor。如此便可以通过代码

parentActorRef!"message from ParentActorRef"

发送消息。

另外,还可以通过

//创建FirstActor对象
  val myactor = system.actorOf(Props[FirstActor], name = "firstActor")
  //获取ActorPath
  val myActorPath=system.child("firstActor")
  //通过system.actorSelection方法获取ActorRef
  val myActor1=system.actorSelection(myActorPath)

system.actorSelection(myActorPath)方法获取相应的ActorRef。然后再使用获取到的ActorRef向Actor发送消息

//向myActor1发送消息
  myActor1!"test"
  myActor1! 123

现在,让我们来总结一下获取ActorRef的方法:

  1. 通过ActorSystem的actorOf方法,不过这种方式是通过创建Actor,然后返回其ActorRef
  2. 通过context.actorOf方法,这种方式也是通过创建Actor,然后返回其ActorRef
  3. 通过context.parent、context.self、context.children方法获取当前Actor的父Actor、当前Actor及子Actor的ActorRef,这种方式是获取已经创建好的Actor的ActorRef
  4. 通过val myActor1=system.actorSelection(myActorPath)方法来获取ActorRef,这种方式也是获取已经创建好的Actor的ActorRef
(二)ActorPath

在前面的例子中,我们通过

val myActorPath=system.child("firstActor")

已经使用到了ActorPath。在Akka中,ActorPath采用统一资源定位符的方式进行组织,例如

//本地ActorPath
"akka://my-sys/user/service-a/worker1" 
//远程ActorPath
"akka.tcp://my-sys@host.example.com:5678/user/service-b"

本地ActorPath是Akka并发编程中的ActorPath表示方式,而远程ActorPath是Akka分布式编程中常用的ActorPath表示方式,”akka.tcp://my-sys@host.example.com:5678/user/service-b”中的TCP表示使用的是TCP协议,也可以使用UPD协议,使用UDP协议的远程ActorPath表示方式有如下形式

"akka.udp://my-sys@host.example.com:5678/user/service-b"

ActorPath当中,akka.udp表示的是使用的协议,my-sys表示的是ActorSytem名称,host.example.com:5678为远程机器地址及端口,user为guardian supervisor,service-b为当前应用程序的顶级Actor(通过system.actorOf方法创建的)

/*
*ActorPath
*/
object Example_08 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class FirstActor extends Actor with ActorLogging{
    //通过context.actorOf方法创建Actor
    var child:ActorRef = _
    override def preStart(): Unit ={
      log.info("preStart() in FirstActor")
      //通过context上下文创建Actor
      child = context.actorOf(Props[MyActor], name = "myActor")
    }
    def receive = {
      //向MyActor发送消息
      case x => child ! x;log.info("received "+x)
    }
  }

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test");
      case _      => log.info("received unknown message");
    }

  }
  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val firstactor = system.actorOf(Props[FirstActor], name = "firstActor")

  //获取ActorPath
  val firstActorPath=system.child("firstActor")
  systemLog.info("firstActorPath--->{}",firstActorPath)

  //通过system.actorSelection方法获取ActorRef
  val myActor1=system.actorSelection(firstActorPath)

  //直接指定其路径
  val myActor2=system.actorSelection("/user/firstActor")
  //使用相对路径
  val myActor3=system.actorSelection("../firstActor")

  systemLog.info("准备向myactor发送消息")
  //向myActor1发送消息
  myActor2!"test"
  myActor2! 123
  Thread.sleep(5000)
  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码运行结果:

[INFO] [04/02/2016 21:04:59.612] [main] [ActorSystem(MyActorSystem)] firstActorPath--->akka://MyActorSystem/user/firstActor
[INFO] [04/02/2016 21:04:59.612] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] preStart() in FirstActor
[INFO] [04/02/2016 21:04:59.615] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 21:04:59.616] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 21:04:59.616] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 21:04:59.616] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor/myActor] received test
[INFO] [04/02/2016 21:04:59.616] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor/myActor] received unknown message

本例中的重点代码如下

//获取ActorPath
  val firstActorPath=system.child("firstActor")
  systemLog.info("myActorPath--->{}",firstActorPath)

  //通过system.actorSelection方法获取ActorRef
  val myActor1=system.actorSelection(firstActorPath)

  //直接指定其路径
  val myActor2=system.actorSelection("/user/firstActor")
  //使用相对路径
  val myActor3=system.actorSelection("../firstActor")

通过 val firstActorPath=system.child(“firstActor”)构造一个ActorPath,然后使用

val myActor1=system.actorSelection(firstActorPath)

获取路径下的ActorRef,除这种方式外,还可以通过绝对路径 val myActor2=system.actorSelection(“/user/firstActor”)及相对路径 val myActor3=system.actorSelection(“../firstActor”)的方式获取ActorRef,需要注意的是绝对路径使用的是guardian supevisor,即/user/firstActor的这种方式。

如果要获取myActor,则获取方法是类似的,例如

//获取ActorPath
val myActorPath=system.child("firstActor").child("myActor")
systemLog.info("firstActorPath--->{}",myActorPath)

//通过system.actorSelection方法获取ActorRef
val myActor1=system.actorSelection(myActorPath)

//直接指定其路径
val myActor2=system.actorSelection("/user/firstActor/myActor")
//使用相对路径
val myActor3=system.actorSelection("../firstActor/myActor")

完整代码如下:

/*
*ActorPath,获取myActor
*/
object Example_09 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class FirstActor extends Actor with ActorLogging{
    //通过context.actorOf方法创建Actor
    var child:ActorRef = _
    override def preStart(): Unit ={
      log.info("preStart() in FirstActor")
      //通过context上下文创建Actor
      child = context.actorOf(Props[MyActor], name = "myActor")
    }
    def receive = {
      //向MyActor发送消息
      case x => child ! x;log.info("received "+x)
    }
  }

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test");
      case _      => log.info("received unknown message");
    }

  }
  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val firstactor = system.actorOf(Props[FirstActor], name = "firstActor")

  //获取ActorPath
  val myActorPath=system.child("firstActor").child("myActor")
  systemLog.info("firstActorPath--->{}",myActorPath)

  //通过system.actorSelection方法获取ActorRef
  val myActor1=system.actorSelection(myActorPath)

  //直接指定其路径
  val myActor2=system.actorSelection("/user/firstActor/myActor")
  //使用相对路径
  val myActor3=system.actorSelection("../firstActor/myActor")

  systemLog.info("准备向myactor发送消息")
  //向myActor1发送消息
  myActor1!"test"
  myActor1! 123
  Thread.sleep(5000)
  //关闭ActorSystem,停止程序的运行
  system.shutdown()
}

代码运行结果:

[INFO] [04/02/2016 21:21:14.377] [main] [ActorSystem(MyActorSystem)] firstActorPath--->akka://MyActorSystem/user/firstActor/myActor
[INFO] [04/02/2016 21:21:14.377] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] preStart() in FirstActor
[INFO] [04/02/2016 21:21:14.381] [main] [ActorSystem(MyActorSystem)] 准备向myactor发送消息
[INFO] [04/02/2016 21:21:14.382] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received test
[INFO] [04/02/2016 21:21:14.382] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received unknown message

关于远程ActorRef的获取,我们将在后续章节中进行讲述。

5. 停止Actor

####(1)通过ActorSystem.shutdown方法停止所有 Actor的运行

/*
*停止Actor:ActorSystem.shutdown方法
*/
object Example_10 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class FirstActor extends Actor with ActorLogging{

    var child:ActorRef = context.actorOf(Props[MyActor], name = "myActor")
    def receive = {
      //向MyActor发送消息
      case x => child ! x;log.info("received "+x)
    }
    override def postStop(): Unit = {
      log.info("postStop In FirstActor")
    }
  }

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test");
      case _      => log.info("received unknown message");
    }
    override def postStop(): Unit = {
      log.info("postStop In MyActor")
    }
  }
  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val firstactor = system.actorOf(Props[FirstActor], name = "firstActor")

  systemLog.info("准备向firstactor发送消息")
  //向firstactor发送消息
  firstactor!"test"
  firstactor! 123
  //关闭ActorSystem,停止所有Acotr运行
  system.shutdown()
}

代码运行结果:

[INFO] [04/02/2016 21:51:34.440] [main] [ActorSystem(MyActorSystem)] 准备向firstactor发送消息
[INFO] [04/02/2016 21:51:34.441] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received test
[INFO] [04/02/2016 21:51:34.441] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 21:51:34.441] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 21:51:34.441] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] received unknown message
[INFO] [04/02/2016 21:51:34.446] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] postStop In MyActor
[INFO] [04/02/2016 21:51:34.447] [MyActorSystem-akka.actor.default-dispatcher-5] [akka://MyActorSystem/user/firstActor] postStop In FirstActor

####(2)通过context.stop方法停止Actor的运行

/*
*停止Actor:context.stop方法
*/
object Example_11 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props

  class FirstActor extends Actor with ActorLogging{

    var child:ActorRef = context.actorOf(Props[MyActor], name = "myActor")
    def receive = {
      case "stop"=>context.stop(child)
      case x =>{
        //向MyActor发送消息
        child ! x
        log.info("received "+x)
      }

    }
    override def postStop(): Unit = {
      log.info("postStop In FirstActor")
    }
  }

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test");
      case _      => log.info("received unknown message");
    }
    override def postStop(): Unit = {
      log.info("postStop In MyActor")
    }
  }
  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val firstactor = system.actorOf(Props[FirstActor], name = "firstActor")

  systemLog.info("准备向firstactor发送消息")
  //向firstactor发送消息
  firstactor!"test"
  firstactor! 123
  firstactor!"stop"

}

代码运行结果:

[INFO] [04/02/2016 22:02:48.760] [main] [ActorSystem(MyActorSystem)] 准备向firstactor发送消息
[INFO] [04/02/2016 22:02:48.761] [MyActorSystem-akka.actor.default-dispatcher-5] [akka://MyActorSystem/user/firstActor/myActor] received test
[INFO] [04/02/2016 22:02:48.761] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 22:02:48.762] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 22:02:48.762] [MyActorSystem-akka.actor.default-dispatcher-5] [akka://MyActorSystem/user/firstActor/myActor] received unknown message
[INFO] [04/02/2016 22:02:48.763] [MyActorSystem-akka.actor.default-dispatcher-5] [akka://MyActorSystem/user/firstActor/myActor] postStop In MyActor

代码的重点为case “stop”=>context.stop(child),直接通过context.stop方法停止Actor的运行。注意程序中并没有使用system.shutdown方法,因此整个程序不会停止。

####(3)通过akka.actor.PoisonPill消息停止Actor的运行

/*
*停止Actor:使用akka.actor.PoisonPill
*/
object Example_12 extends App{
  import akka.actor.Actor
  import akka.actor.ActorSystem
  import akka.actor.Props
  import akka.actor.PoisonPill

  class FirstActor extends Actor with ActorLogging{

    var child:ActorRef = context.actorOf(Props[MyActor], name = "myActor")
    def receive = {
      //向child发送PoisonPill停止其运行
      case "stop"=>child!PoisonPill
      case x =>{
        //向MyActor发送消息
        child ! x
        log.info("received "+x)
      }

    }
    override def postStop(): Unit = {
      log.info("postStop In FirstActor")
    }
  }

  class MyActor extends Actor with ActorLogging{
    def receive = {
      case "test" => log.info("received test");
      case _      => log.info("received unknown message");
    }
    override def postStop(): Unit = {
      log.info("postStop In MyActor")
    }
  }
  val system = ActorSystem("MyActorSystem")
  val systemLog=system.log

  //创建FirstActor对象
  val firstactor = system.actorOf(Props[FirstActor], name = "firstActor")

  systemLog.info("准备向firstactor发送消息")
  //向firstactor发送消息
  firstactor!"test"
  firstactor! 123
  firstactor!"stop"

}

代码运行结果:

[INFO] [04/02/2016 22:12:09.947] [main] [ActorSystem(MyActorSystem)] 准备向firstactor发送消息
[INFO] [04/02/2016 22:12:09.947] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor/myActor] received test
[INFO] [04/02/2016 22:12:09.947] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] received test
[INFO] [04/02/2016 22:12:09.947] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor] received 123
[INFO] [04/02/2016 22:12:09.947] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor/myActor] received unknown message
[INFO] [04/02/2016 22:12:09.951] [MyActorSystem-akka.actor.default-dispatcher-4] [akka://MyActorSystem/user/firstActor/myActor] postStop In MyActor

代码与Exampel_11中的不同之处在于

//向child发送PoisonPill停止其运行
case "stop"=>child!PoisonPill

它使用不是context.stop方法,而是向MyActor发送了PoisonPill消息,其它代码不变。

还有一种gracefulStop方法可以停止Actor的运行,这部分内容等了解完Future类的作用原理之后再来讨论。

6. 消息发送

Akka提供了两种消息模型:fire-and-forget和Send-And-Receive-Future。

fire-and-forget是一种单向消息发送模型,指的是异步发送消息,通过异步发送消息且消息发送后可以立即返回,Akka中使用!方法进行fire-and-forget消息发送,如stringActor!”Creating Actors with implicit val context”,它的意思是当前发送方Actor向stringActor发送字符串消息”Creating Actors with implicit val context”,发送完该消息后立即返回,而无需等待stringActor的返回,!还有个重载的方法tell;

Send-And-Receive-Future是一种双向消息发送模型,指的是异步发送消息,向目标Actor发送完消息后,然后返回一个Future作为后期可能的返回,当前发送方Actor将等待目标Actor的返回,Akka中使用?方法进行Send-And-Receive-Future消息的发送,它也同样有一个重载的方法ask

1. !消息发送,Fire-and-Forget消息模型
/**
  * 消息处理:!(Fire-Forget)
  */
object Example12 extends  App{
  import akka.actor.Actor
  import akka.actor.Props
  import akka.event.Logging
  import akka.actor.ActorSystem

 //定义几种不同的消息
  case class Start(var msg:String)
  case class Run(var msg:String)
  case class Stop(var msg:String)

  class ExampleActor extends Actor {
    val other = context.actorOf(Props[OtherActor], "OtherActor")
    val log = Logging(context.system, this)
    def receive={
      //使用fire-and-forget消息模型向OtherActor发送消息,隐式地传递sender
      case Start(msg) => other ! msg
      //使用fire-and-forget消息模型向OtherActor发送消息,直接调用tell方法,显式指定sender
      case Run(msg) => other.tell(msg, sender)
    }
  }

  class OtherActor extends  Actor{
    val log = Logging(context.system, this)
    def receive ={
      case s:String=>log.info("received message:\n"+s)
      case _      ⇒ log.info("received unknown message")
    }
  }

  //创建ActorSystem,ActorSystem为创建和查找Actor的入口
  //ActorSystem管理的Actor共享配置信息如分发器(dispatchers)、部署(deployments)等
  val system = ActorSystem("MessageProcessingSystem")

  //创建ContextActor
  val exampleActor = system.actorOf(Props[ExampleActor],name="ExampleActor")

  //使用fire-and-forget消息模型向exampleActor发送消息
  exampleActor!Run("Running")
  exampleActor!Start("Starting")

  //关闭ActorSystem
  system.shutdown()
}

代码运行结果如下:

[INFO] [03/20/2016 20:57:43.665] [MessageProcessingSystem-akka.actor.default-dispatcher-5] [akka://MessageProcessingSystem/user/ExampleActor/OtherActor] received message:
Running
[INFO] [03/20/2016 20:57:43.672] [MessageProcessingSystem-akka.actor.default-dispatcher-5] [akka://MessageProcessingSystem/user/ExampleActor/OtherActor] received message:
Starting

在ExampleActor中,通过隐式变量context创建了OtherActor实例:val other = context.actorOf(Props[OtherActor], “OtherActor”),在ExampleActor的receive方法中,处理两种不同类型的消息例如:

//使用fire-and-forget消息模型向OtherActor发送消息,隐式地传递sender
  case Start(msg) => other ! msg
  //使用fire-and-forget消息模型向OtherActor发送消息,直接调用tell方法,显式指定sender
  case Run(msg) => other.tell(msg, sender)

处理Start类型的消息时,直接使用!进行消息发送,而处理Run类型的消息时,使用的是tell方法,可以看到使用tell方法需要显式地指定其sender,而使用!进行消息发送则不需要,事实上!方法通过隐式值传入需要的Sender,对比!与tell方法的定义便很容易理解

//!方法的定义
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
//tell方法的定义
final def tell(msg: Any, sender: ActorRef): Unit = this.!(msg)(sender)

可以看到,tell方法的实现依赖于!方法。如果在一个Actor当中使用!方法时,例如ExampleActor中使用的other ! msg向OtherActor发送消息,则sender隐式为ExampleActor,如果不是在Actor中使用则默认为Actor.noSender,即sender为null。

2. ?消息发送,Send-And-Receive-Future消息模型

理解了Fire-And-Forget消息模型后,接着对Send-And-Receive-Future消息模型进行介绍,下面的代码给出了其使用示例。

/**
  * 消息处理:?(Send-And-Receive-Future)
  */
object Example13 extends  App{
  import akka.actor.Actor
  import akka.actor.Props
  import akka.event.Logging
  import akka.actor.ActorSystem
  import scala.concurrent.Future
  import akka.pattern.ask
  import akka.util.Timeout
  import scala.concurrent.duration._
  import akka.pattern.pipe
  import scala.concurrent.ExecutionContext.Implicits.global

  //消息:个人基础信息
  case class BasicInfo(id:Int,val name:String, age:Int)
  //消息:个人兴趣信息
  case class InterestInfo(id:Int,val interest:String)
  //消息: 完整个人信息
  case class Person(basicInfo: BasicInfo,interestInfo: InterestInfo)

  //基础信息对应Actor
  class BasicInfoActor extends Actor{
    val log = Logging(context.system, this)
    def receive = {
      //处理送而来的用户ID,然后将结果发送给sender(本例中对应CombineActor)
      case id:Int ⇒log.info("id="+id);sender!new BasicInfo(id,"John",19)
      case _      ⇒ log.info("received unknown message")
    }
  }

  //兴趣爱好对应Actor
  class InterestInfoActor extends Actor{
    val log = Logging(context.system, this)
    def receive = {
      //处理发送而来的用户ID,然后将结果发送给sender(本例中对应CombineActor)
      case id:Int ⇒log.info("id="+id);sender!new InterestInfo(id,"足球")
      case _      ⇒ log.info("received unknown message")
    }
  }

  //Person完整信息对应Actor
  class PersonActor extends Actor{
    val log = Logging(context.system, this)
    def receive = {
      case person: Person =>log.info("Person="+person)
      case _      ⇒ log.info("received unknown message")
    }
  }

  class CombineActor extends Actor{
    implicit val timeout = Timeout(5 seconds)
    val basicInfoActor = context.actorOf(Props[BasicInfoActor],name="BasicInfoActor")
    val interestInfoActor = context.actorOf(Props[InterestInfoActor],name="InterestInfoActor")
    val personActor = context.actorOf(Props[PersonActor],name="PersonActor")
    def receive = {
      case id: Int =>
        val combineResult: Future[Person] =
          for {
            //向basicInfoActor发送Send-And-Receive-Future消息,mapTo方法将返回结果映射为BasicInfo类型
            basicInfo <- ask(basicInfoActor, id).mapTo[BasicInfo]
            //向interestInfoActor发送Send-And-Receive-Future消息,mapTo方法将返回结果映射为InterestInfo类型
            interestInfo <- ask(interestInfoActor, id).mapTo[InterestInfo]
          } yield Person(basicInfo, interestInfo)

        //将Future结果发送给PersonActor
       pipe(combineResult).to(personActor)
    }
  }

  val _system = ActorSystem("Send-And-Receive-Future")
  val combineActor = _system.actorOf(Props[CombineActor],name="CombineActor")
  combineActor ! 12345
  Thread.sleep(5000)
  _system.shutdown

}

代码运行结果如下:

[INFO] [03/20/2016 22:55:11.208] [Send-And-Receive-Future-akka.actor.default-dispatcher-3] [akka://Send-And-Receive-Future/user/CombineActor/BasicInfoActor] id=12345
[INFO] [03/20/2016 22:55:11.220] [Send-And-Receive-Future-akka.actor.default-dispatcher-2] [akka://Send-And-Receive-Future/user/CombineActor/InterestInfoActor] id=12345
[INFO] [03/20/2016 22:55:11.223] [Send-And-Receive-Future-akka.actor.default-dispatcher-4] [akka://Send-And-Receive-Future/user/CombineActor/PersonActor] Person=Person(BasicInfo(12345,John,19),InterestInfo(12345,足球))

代码中定义了3种类型的消息,分别是个人基础信息case class BasicInfo(id:Int,val name:String, age:Int)、个人兴趣信息case class InterestInfo(id:Int,val interest:String)以及完整个人信息case class Person(basicInfo: BasicInfo,interestInfo: InterestInfo),然后为这3种类型的消息定义了相应的Actor即BasicInfoActor、InterestInfoActor和PersonActor,在CombineActor分别创建相应Actor的实例,receive方法中使用ask向BasicInfoActor、InterestInfoActor发送Send-And-Receive-Future模型消息,BasicInfoActor、InterestInfoActor中的receive方法接收到发送来的Int类型消息并分别使用!向CombineActor发送BasicInfo、InterestInfo消息,将结果保存在Future[Person]中,然后通过代码pipe(combineResult).to(personActor)将结果发送给PersonActor。

三、Type Actor

1. Typed Actor定义

Akka中的Typed Actor是Active Objects设计模式的实现,Active Objects模式将方法的执行和方法的调用进行解耦合,从而为程序引入并发性。Typed Actor由公用的接口和对应实现两部分构成,其后面深层次的实现使用的是代理模式,即通过使用JDK中的动态代理来实现,在调用接口的方法时自动分发到实现接口的对象上。Typed Actor的定义[ ]如下所示。

trait Squarer {
    //fire-and-forget消息
    def squareDontCare(i: Int): Unit
    //非阻塞send-request-reply消息
    def square(i: Int): Future[Int]
    //阻塞式的send-request-reply消息
    def squareNowPlease(i: Int): Option[Int]
    //阻塞式的send-request-reply消息
    def squareNow(i: Int): Int
  }

  class SquarerImpl(val name: String) extends Squarer {
    def this() = this("SquarerImpl")

    def squareDontCare(i: Int): Unit = i * i
    def square(i: Int): Future[Int] = Promise.successful(i * i).future
    def squareNowPlease(i: Int): Option[Int] = Some(i * i)
    def squareNow(i: Int): Int = i * i
  }

2. 创建Typed Actor

通过下列代码创建Typed Actor实例。

//直接通过默认的构造函数创建Typed Actor
val mySquarer: Squarer =TypedActor(system).typedActorOf(TypedProps[SquarerImpl]())
//直接通过默认的构造函数创建Typed Actor并指定Typed Actor名称
val mySquarer: Squarer =TypedActor(system).typedActorOf(TypedProps[SquarerImpl](),"mySquarer")
//通过非默认的构造函数创建Typed Actor并指定Typed Actor名称
val otherSquarer: Squarer = TypedActor(system).typedActorOf(TypedProps(classOf[Squarer],new SquarerImpl("SquarerImpl")), "otherSquarer")

上面代码演示的是使用构造函数和非默认构造函数创建Typed Actor,其中Squarer为代理的类型,SquarerImpl为具体实现的类型。

3. 消息发送

//fire-forget消息发送
mySquarer.squareDontCare(10)

//send-request-reply消息发送
val oSquare = mySquarer.squareNowPlease(10)

val iSquare = mySquarer.squareNow(10)

//Request-reply-with-future 消息发送
val fSquare = mySquarer.square(10)
val result = Await.result(fSquare, 5 second)

代码mySquarer.squareDontCare(10)是单向消息发送,方法将在另外一个线程上异步地执行;val oSquare = mySquarer.squareNowPlease(10)val iSquare = mySquarer.squareNow(10)为Request-reply消息发送,在特定时间内以阻塞的方式执行,对于squareNowPlease(10)方法如果在对应时间内没有返回结果则返回值为None,否则返回值为Option[Int]类型,对于squareNow(10)方法如果在对应时间内无返回值则会抛出异常java.util.concurrent.TimeoutException,否则返回Int类型值;val fSquare = mySquarer.square(10)为Request-reply-with-future式的消息发送,以非阻塞的方式执行,可以通过val result = Await.result(fSquare, 5 second)获取执行结果。完整代码如下所示。

/*
 * Typed Actor
 */
object Example_01 extends  App {

  import akka.event.Logging
  import scala.concurrent.{ Promise, Future }
  import akka.actor.{ TypedActor, TypedProps }
  import scala.concurrent.duration._

  trait Squarer {
    //fire-and-forget消息
    def squareDontCare(i: Int): Unit
    //非阻塞send-request-reply消息
    def square(i: Int): Future[Int]
    //阻塞式的send-request-reply消息
    def squareNowPlease(i: Int): Option[Int]
    //阻塞式的send-request-reply消息
    def squareNow(i: Int): Int
  }

  class SquarerImpl(val name: String) extends Squarer {
    def this() = this("SquarerImpl")

    def squareDontCare(i: Int): Unit = i * i
    def square(i: Int): Future[Int] = Promise.successful(i * i).future
    def squareNowPlease(i: Int): Option[Int] = Some(i * i)
    def squareNow(i: Int): Int = i * i
  }

  val system = ActorSystem("TypedActorSystem")
  val log = Logging(system, this.getClass)

  //使用默认构造函数创建Typed Actor
  val mySquarer: Squarer =
    TypedActor(system).typedActorOf(TypedProps[SquarerImpl](),"mySquarer")

  //使用非默认构造函数创建Typed Actor
    val otherSquarer: Squarer =
      TypedActor(system).typedActorOf(TypedProps(classOf[Squarer],
        new SquarerImpl("SquarerImpl")), "otherSquarer")

  //fire-forget消息发送
  mySquarer.squareDontCare(10)

  //send-request-reply消息发送
  val oSquare = mySquarer.squareNowPlease(10)

  log.info("oSquare="+oSquare)

  val iSquare = mySquarer.squareNow(10)
  log.info("iSquare="+iSquare)

  //Request-reply-with-future 消息发送
  val fSquare = mySquarer.square(10)
  val result = Await.result(fSquare, 5 second)

  log.info("fSquare="+result)

  system.shutdown()
}

代码运行结果如下:

[INFO] [03/21/2016 21:15:50.592] [main] [Example12_9(akka://TypedActorSystem)]oSquare=Some(100)[INFO][03/21/201621:15:50.649][main][Example129(akka://TypedActorSystem)] iSquare=100   
[INFO] [03/21/2016 21:15:50.649] [main] [Example12_9$(akka://TypedActorSystem)] fSquare=100

4. 停止运行Typed Actor

当Typed Actor不再需要时要将其停止,有3种方法停止Typed Actor的运行:

  1. 通过system.shutdown()停止ActorSystem中所有的Typed Actor;
  2. 调用TypedActor(system).stop(mySquarer)停止指定的Typed Actor;
  3. 调用TypedActor(system).poisonPill(otherSquarer)停止指定的Typed Actor。

具体使用代码如下:

/*
 * 停止Typed Actor
 */
object Example_3 extends  App {

  import akka.event.Logging
  import scala.concurrent.{ Promise, Future }
  import akka.actor.{ TypedActor, TypedProps }
  import scala.concurrent.duration._

  trait Squarer {
    //fire-and-forget消息
    def squareDontCare(i: Int): Unit
    //非阻塞send-request-reply消息
    def square(i: Int): Future[Int]
    //阻塞式的send-request-reply消息
    def squareNowPlease(i: Int): Option[Int]
    //阻塞式的send-request-reply消息
    def squareNow(i: Int): Int
  }

  //混入PostStop和PreStart
  class SquarerImpl(val name: String) extends  Squarer with PostStop with PreStart {
    import TypedActor.context
    val log = Logging(context.system,TypedActor.self.getClass())
    def this() = this("SquarerImpl")

    def squareDontCare(i: Int): Unit = i * i
    def square(i: Int): Future[Int] = Promise.successful(i * i).future
    def squareNowPlease(i: Int): Option[Int] = Some(i * i)
    def squareNow(i: Int): Int = i * i

    def postStop(): Unit={
      log.info ("TypedActor Stopped")
    }
    def preStart(): Unit={
      log.info ("TypedActor  Started")
    }
  }

  val system = ActorSystem("TypedActorSystem")
  val log = Logging(system, this.getClass)

  //使用默认构造函数创建Typed Actor
  val mySquarer: Squarer =
    TypedActor(system).typedActorOf(TypedProps[SquarerImpl](),"mySquarer")

  //使用非默认构造函数创建Typed Actor
  val otherSquarer: Squarer =
    TypedActor(system).typedActorOf(TypedProps(classOf[Squarer],
      new SquarerImpl("SquarerImpl")), "otherSquarer")

  //Request-reply-with-future 消息发送
  val fSquare = mySquarer.square(10)
  val result = Await.result(fSquare, 5 second)
  log.info("fSquare="+result)

  //调用poisonPill方法停止Actor运行
  TypedActor(system).poisonPill(otherSquarer)

  //调用stop方法停止Actor运行
  TypedActor(system).stop(mySquarer)

  //system.shutdown()
}

代码运行结果如下所示。

[INFO] [03/21/2016 22:41:51.119] [TypedActorSystem-akka.actor.default-dispatcher-2] [$Proxy0(akka://TypedActorSystem)] TypedActor  Started
[INFO] [03/21/2016 22:41:51.123] [TypedActorSystem-akka.actor.default-dispatcher-2] [$Proxy1(akka://TypedActorSystem)] TypedActor  Started
[INFO] [03/21/2016 22:41:51.124] [main] [Example12_10$(akka://TypedActorSystem)] fSquare=100
[INFO] [03/21/2016 22:41:51.131] [TypedActorSystem-akka.actor.default-dispatcher-5] [$Proxy1(akka://TypedActorSystem)] TypedActor Stopped
[INFO] [03/21/2016 22:41:51.131] [TypedActorSystem-akka.actor.default-dispatcher-3] [$Proxy0(akka://TypedActorSystem)] TypedActor Stopped

代码中类SquarerImpl 混入了PreStart和PostStop两个trait:class SquarerImpl(val name: String) extends Squarer with PostStop with PreStart,这样的话在创建TypedActor之前和停止TypedActor后能够进行相应的操作,本例中主要是为监视TypedActor的创建和停止过程。代码TypedActor(system).stop(mySquarer)通过stop方法停止TypedActor,而TypedActor(system).poisonPill(otherSquarer)通过调用poisonPill方法停止运行TypedActor。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值