Akka实战:快速构建高可用分布式应用
上QQ阅读APP看书,第一时间看更新

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实例,真正做到“一次模板,到处创建”!