Actor简介(二)

       在上一部分,我们已经介绍了actor的基本用法,知道如何创建一个actor。我相信大家肯定不想局限于如此,一定想把actor紧紧的握在手中,掌握它的生死。come on ,来吧!

     Actor生命周期

       线程在运行历程中,会经历创建、准备、等待、阻塞等阶段,这一系列我们称之为生命周期。当然,actor也有它自己的生命历程,比如创建、运行、重启和销毁等。在理想的情况下,Actor会任劳任怨的不停工作,但在实际情况下,网络超时或者程序异常,我们希望能够及时感知并处理,例如actor重启或停止执行。关于Actor的生命周期,我们先来看一张图(来自Akka官网):

        从图中,我们看出Actor的生命周期主要包含创建并启动(Start)、恢复(Resume)、重启(Restart)、停止(Stop)这几个阶段,针对自身状态变化,各阶段主要有如下行为:

阶段

行为

创建并启动

(Start)

actorOf()创建并启动Actor时,指定对象Path和UID(对象唯一标识,通过getSelf().path().uid()获取),默认执行preStart()方法,我们可以在该方法中进行资源初始化。

恢复

(Resume)

actor出现异常时,在容错机制下,可以让actor恢复并继续执行,此时actor会继续使用之前的对象实例,状态也回保留。

重启

(Restart)

重启会经历两个过程: 1.调用旧实例的preRestart()方法,该方法会默认停掉所有子级actor并调用postStop()方法。 2.创建新实例,在新实例上调用postRestart()方法, 该方法默认会调用preStart()方法。 重启之后,path和UID不变,ActorRef不变,但是自身状态已改变。

停止

(Stop)

停止Actor,会调用postStop()方法,同时会发送一条Terminated信息给自己的监控者。

     创建并启动

      上一章节,我们已经知道如何创建Actor对象,现在不做过多介绍,上一章节的ActorDemo类上,我们重写preStart:

    @Override
    public void preStart() throws Exception {
        System.out.println("actorDemo开始启动");
    }

      大家执行可以发现,当actor在启动的时候,默认就会执行该方法,针对这个特性,我们可以在该方法做一些资源初始化或创建子类Actor。

     停止

       在我们的程序里,出现某种异常或当我们不需要actor时,我们可以停止actor的执行。在Akka中,停止一个actor相当的简单,有三种方式供我们选择,如下:

第一种

调用ActorSystem或者ActorContext的stop方法

第二种

给Actor发送PoisonPill(毒药丸)

第三种

发送一条kill消息给Actor,此时会抛出ActorKilledException异常,另外会通知父级做相应处理。

创建一个Actor,我们演示这三种停止方式:

public class StopActor extends AbstractActor {
    private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);

    @Override
    public Receive createReceive() {
        return receiveBuilder().match(Object.class,s->{
            log.info(s.toString());
        }).build();
    }

    @Override
    public void postStop() throws Exception {
        log.info("actor stop");
    }

    public static void main(String[] args) {
        ActorSystem system = ActorSystem.create("system");
        ActorRef stopActor = system.actorOf(Props.create(StopActor.class), "stopActor");
        //第一种:ActorSystem停止actor
        //system.stop(stopActor);
        //第二种
        stopActor.tell(PoisonPill.getInstance(),ActorRef.noSender());
        //第三种
        //stopActor.tell(Kill.getInstance(),ActorRef.noSender());
    }
}

        使用第一种或第二种方式,我们会收到如下结果:

[INFO] [10/03/2018 20:57:38.517] [system-akka.actor.default-dispatcher-3] [akka://system/user/stopActor] actor stop

        当使用第三种方式时,我们会多收到一条ERROR级别的消息:

[ERROR] [10/03/2018 20:56:06.261] [system-akka.actor.default-dispatcher-3] [akka://system/user/stopActor] Kill (akka.actor.ActorKilledException: Kill)

        使用kill停止actor,会抛出ActorKilledException异常,上报到父级,父级默认处理就是停止Actor。

        我们发现,使用Akka提供的API,我们可以很简单的停止actor,但是大家想过没有,在实际项目中,我们的业务是复杂多样的,我们需要考虑停止actor是否会带来不可预知的后果。其实大家也不必太过担心,Actor在停止时已经有一套相当可靠的流程:

       1.停止actor时,会先将正常的消息处理完毕后,再完全停止,后续消息将不再处理,邮箱(保留Actor消息)将被挂起。

       2.给子级发送停止消息,需等待子级全部停止,再停掉自己,停止后会调用postStop方法,释放资源。

       3.向生命周期监控者(DeathWatch)发送Terminated消息,以便监控者做相应处理。

       下面我们写一个监控actor,来显示这个流程:

public class WatchActor extends AbstractActor {
    private final LoggingAdapter logger = Logging.getLogger(getContext().getSystem(), this);
    private ActorRef stopActor;
    @Override
    public Receive createReceive() {
        return receiveBuilder().match(Object.class, o -> {
            logger.info(o.toString());
        }).build();
    }

    @Override
    public void preStart() throws Exception {
        //初始化子类stopActor
        stopActor=getContext().actorOf(Props.create(StopActor.class),"stopActor");
    }

    @Override
    public void postStop() throws Exception {
        logger.info("watchActor stop");
    }

    public static void main(String[] args) {
        ActorSystem system = ActorSystem.create("system");
        ActorRef watchActor = system.actorOf(Props.create(WatchActor.class), "watchActor");
        system.stop(watchActor);
    }
}

        此时,当我们执行main方法时,会输出如下内容:

[INFO] [10/04/2018 08:52:24.549] [system-akka.actor.default-dispatcher-4] [akka://system/user/watchActor/stopActor] actor stop

[INFO] [10/04/2018 08:52:24.551] [system-akka.actor.default-dispatcher-2] [akka://system/user/watchActor] watchActor stop

         结果表明,子级actor停止后,才会停止父类actor。

        有时,我们需要监控子级actor,针对子级actor的生命周期,做出不同的处理。监控actor可以使用getContext().watch方法(相反,getContext().unwatch取消监控),对WatchActor稍作修改,如下:

  public static void main(String[] args) {
        ActorSystem system = ActorSystem.create("system");
        ActorRef watchActor = system.actorOf(Props.create(WatchActor.class), "watchActor");
        watchActor.tell("stopActor",ActorRef.noSender());
       // system.stop(watchActor);
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder().match(String.class, s -> {
            if ("stopActor".equals(s)) {
                getContext().stop(stopActor);
            }
        }).match(Terminated.class, t -> {
            logger.info("监控到" + t.getActor() + "停止了");
        }).build();
    }

    @Override
    public void preStart() throws Exception {
        //初始化子类stopActor
        stopActor = getContext().actorOf(Props.create(StopActor.class), "stopActor");
        //用于监控actor
        getContext().watch(stopActor);
    }

        此时,执行main方法,我们会得到如下结果:

[INFO] [10/04/2018 09:00:20.805] [system-akka.actor.default-dispatcher-4] [akka://system/user/watchActor/stopActor] actor stop

[INFO] [10/04/2018 09:00:20.809] [system-akka.actor.default-dispatcher-3] [akka://system/user/watchActor] 监控到Actor[akka://system/user/watchActor/stopActor#-213431265]停止了

       结果表明,子级actor停止,会给父类发送一条Terminated消息。

       在实际项目中,由于各种原因,例如网络、代码、服务器等都可能出现问题,为了程序的高可用,就涉及到监督和容错处理。这一块内容就会涉及到我们的actor在出现异常时,是使用重启、恢复、还是停止,限于篇幅,我们下一节继续。

猜你喜欢

转载自blog.csdn.net/p_programmer/article/details/82935552