转载自Spark修炼之道
多核处理器的出现使并发编程(Concurrent Programming)成为开发人员必备的一项技能,许多现代编程语言都致力于解决并发编程问题。并发编程虽然能够提高程序的性能,但传统并发编程的共享内存通信机制对开发人员的编程技能要求很高,需要开发人员通过自身的专业编程技能去避免死锁、互斥等待及竞争条件(Race Condition)等,熟悉Java语言并发编程的读者们对这些问题的理解会比较深刻,这些问题使得并发编程比顺序编程要困难得多。
Scala语言并没有直接使用Java语言提供的并发编程库,而是通过Actor模型来解决Java并发编程中遇到的各种问题,为并发编程提供了更高级的抽象。
并发和并行从宏观来看,都是为进行多任务运行,但并发(Concurrency)和并行(parallelism)两者之间是有区别的。
图1 进程、线程模型
图1给出了多核处理器下的现代操作系统进程和线程模型,图中进程2的线程1被调用度到处理器的核2上运行、进程3的线程1被调度到处理器的核3上运行,进程2的线程1和进程3的线程1是并行的,它们可以同时运行,而进程1的线程1和线程2都调度到处理器的核1上运行,此外它们还共享线程1的内存空间,在运行时面临着资源竞争包括CPU、内存及其它如IO等,它们在同一时候只能运行一个,但在一段时间内都可以运行,因此进程1的线程1和线程2是并发执行的。
传统的Java并发编程模型不容易进行纵向扩展,因此并发的线程数越多,程序行为便会变得很难理解和控制,更多的线程加入到资源竞争,出现死锁等情况的概率增加。横向扩展比纵向扩展困难更大,此时的程序变为分布式环境下的应用,情况更为复杂,对开发人员的要求更高。Scala提供的Actor模型可以解决并发应用程序的横向扩展和纵向扩展问题,如图2、图3给出了基本Actor模型的横向扩展和纵向扩展。
图2 纵向扩展
图3 横向扩展
在使用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模型是对现实世界的高度抽象,它具有如下特点:
Scala语言中原生地支持Actor模型,只不过功能还不够强大,从Scala 2.10版本之后,Akka框架成为Scala包的一部分,可以在程序中直接使用。Akka框架是一个以Actor模型为基础构建的基于事件的并发编程框架,底层使用Scala语言实现,提供Java和Scala两种API,它属于LightBend公司(原Typesafe公司)体系结构的一部分,如图5所示。
图5 Lightbend 体系结构
Akka框架意在简化高并发、可扩展及分布式应用程序的设计,它具有如下优势:
Akka框架由下列十个组件构成:
通过扩展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类似,这里不再赘述。
在前面两个例子中,通过
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.(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
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 */ }
}
/*
*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方法。
整体代码如下:
/*
*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”
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容错部分进行讲解。
下图是Akka官方文档中给出的一张图
该图清晰地说明了ActorPath,ActorRef,Actor及ActorSystem之间的关系,并说明了Actor整体的层次结构。前面我们提到,Akka应用程序会持有一个名称为user的Actor,该Actor被称为guardian supervisor(守卫监督器),无论是ActorSystem创建的Actor还是通过ActorContext创建的Actor都为user的子类,它是最顶级的Actor。
对于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的方法:
在前面的例子中,我们通过
val myActorPath=system.child("firstActor")
已经使用到了ActorPath。在Akka中,ActorPath采用统一资源定位符的方式进行组织,例如
//本地ActorPath
"akka://my-sys/user/service-a/worker1"
//远程ActorPath
"akka.tcp://[email protected]:5678/user/service-b"
本地ActorPath是Akka并发编程中的ActorPath表示方式,而远程ActorPath是Akka分布式编程中常用的ActorPath表示方式,”akka.tcp://[email protected]:5678/user/service-b”中的TCP表示使用的是TCP协议,也可以使用UPD协议,使用UDP协议的远程ActorPath表示方式有如下形式
"akka.udp://[email protected]: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的获取,我们将在后续章节中进行讲述。
####(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类的作用原理之后再来讨论。
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
/**
* 消息处理:!(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。
理解了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。
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
}
通过下列代码创建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为具体实现的类型。
//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
当Typed Actor不再需要时要将其停止,有3种方法停止Typed Actor的运行:
system.shutdown()
停止ActorSystem中所有的Typed Actor;TypedActor(system).stop(mySquarer)
停止指定的Typed Actor;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。