
2.3 创建一个Actor
2.3.1 定义Actor
在Akka中,Actor大致分为两类:UntypedActor和TypedActor,它们的使用场景有比较明显的区别。
❑ UntypedActor:基于经典的Actor模型实现,能完整表达Akka-Actor的设计思想,推荐使用它来定义Actor。
❑ TypedActor:会比较方便地把正常OOP的代码包装成异步执行的Actor,比较符合程序员的API调用逻辑,但是不太能表达基于消息的处理方式,在一般情况下,不推荐使用它。
这里我们通过继承UntypedActor来自定义一个Actor,示例代码如下:
import akka.actor.UntypedActor; import akka.event.Logging; import akka.event.LoggingAdapter; public class ActorDemo extends UntypedActor { private LoggingAdapter log =Logging.getLogger( this .getContext().system(), this); @Override public void onReceive(Object msg ) throws Exception { if (msg instanceof String) { log.info( msg.toString()); } else { unhandled( msg); } } }
代码解释:
1)onReceive方法是用于接收并处理消息的地方,这里我们通过类型做了简单的匹配,当匹配不到相应的消息类型时,推荐使用unhandled进行处理;
2)在做Actor的调试时,可以采用LoggingAdapter来进行日志打印,以便获得更详细的信息。
2.3.2 创建Actor实例
我们看到,自定义一个Actor如此的简单,那么怎样使用它呢?正常情况下,我们通过new的方式就可以得到一个对象的引用,然后通过该引用来调用相应的API,如下所示:
ActorDemo ad =new ActorDemo();
当我们执行这行代码时,系统会抛出如下异常:
Exception in thread "main" akka.actor.ActorInitializationException : You cannot create an instance of [com.javaakka.utpactor.ActorDemo] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor
很明显,这里不能通过new的方式得到一个Actor的引用。这正是Akka的有意为之,笔者之前介绍过,Actor作为核心执行单元,天生拥有并行运行和分布式的特点,为了屏蔽实现细节(包括对内部状态的封装),简化调用方式(特别是位置透明性),统一资源调配和层级管理,我们只能通过Akka提供的某些API才能创建或查找Actor。
一般来说,我们会通过ActorSystem或ActorContext来创建Actor:
//参数为ActorSystem的名字,可以不传 ActorSystem system =ActorSystem. create( "sys" ); //参数分别是构造器和Actor的名字,名字可以不传 ActorRef actorRef =system.actorOf(Props.create(ActorDemo.class), "actorDemo" );
代码解释:
1)ActorSystem是一个比较重量级的对象,一般来说每个应用程序只需要创建一个该对象;在创建ActorSystem和Actor时,最好都给出名字,在该示例中,它们的名字分别是sys和actorDemo,同时要注意,在同一个ActorSystem中Actor不能重名;
2)actorOf返回的不是Actor本身,而是ActorRef,即Actor的引用,我们就是通过该引用来进行消息通信的;
3)假如Actor的构造函数有参数,可以通过create方法传入。
通过ActorSystem创建的是一个顶级的Actor(即/user路径下),但是在实际项目中,我们可能希望创建具有层级关系的Actor网络,然后把任务交给子Actor去处理,并且让父级去监督子级。
此时我们可以使用Actor中的getContext来创建子Actor:
ActorRef childActor=getContext().actorOf(Props. create(ChildActor.class), "childActor" );
2.3.3 工厂模式---Props/Creator
ActorSystem和ActorContext通过接收一个Props实例来创建Actor,而Props实例本身有两种方式可以创建:
❑ 指定Actor的Class,比如:Props.create(ActorDemo.class);
❑ 指定一个Actor工厂,实现akka.japi.Creator接口,重写其create方法。
对于比较简单的场景,直接使用第一种即可,而对于需要统一配置或创建某个Actor的场景,则需要自己实现Creator工厂接口,一般来讲,我们会把该工厂直接定义在Actor内部,作为静态API存在,下面是示例代码:
class PropsDemoActor extends UntypedActor{ @Override public void onReceive(Object msg) throws Exception { } public static Props createProps() { //实现Creator接口并传入Props.create方法 return Props.create(new Creator<PropsDemoActor>(){ @Override public PropsDemoActor create() throws Exception { //创建Actor return new PropsDemoActor(); } }); }}
此时创建该Actor的方式如下:
ActorRef ref = system.actorOf(PropsDemoActor.createProps(), "propsActor");
有些时候,我们可能会把生成的Props实例当作消息传给其他Actor,让对方“帮”我们创建符合要求的Actor实例,真正做到“一次模板,到处创建”!