在如今的IT行业中,如果一个系统不具备高可用以及稳定性,那么它迟早会被淘汰的。也许有时出现故障并不是代码本身的问题,网络(连接超时、读超时等)、服务器故障、用户操作有误等原因,也不能影响用户的体验。程序发生故障时,我们要尽可能保证用户体验不受影响,这就需要我们的系统具有良好的容错机制,能保证服务在不可用时自我修复。
监督策略
Actor作为Akka中最基本的执行单元,是所有业务的核心,如若发生故障,可能产生不可估量的后果,这就需要拥有一套容错机制。前面我们已经提到,Actor系统具有监控机制,父级对子级进行管理,子级如果出现异常情况,父级可以通过处理逻辑来确定子级是重启、恢复、还是停止。
Akka提供了两种监督策略,分别是One-For-One Strategy和All-For-One Strategy。关于这两种策略的区别如下:
在程序中,当我们没有为Actor指定管理策略,Akka提供了一套默认的策略:抛出ActorInitializationException、ActorKilledException 、DeathPactException 时,停止子Actor运行。
抛出Exception时,重启子Actor。
其它类型的Throwable异常时,会上溯到父级。
实际项目中,我们可能自定义了许多异常,以此来区分不同的情况。针对这些不同的异常,我们处理Actor方式或许不一样,这就需要我们自定义监督策略。SupervisorStrategy是自定义监督策略核心类,构造函数如下:public OneForOneStrategy(int maxNrOfRetries, java.time.Duration withinTimeRange, PartialFunction decider)
我们可以看到它有三个参数,分别是maxNrOfRetries、withinTimeRange、decider。其中maxNrOfRetries和withinTimeRange表明在一段时间的重启次数,超过次数则stop。第三个参数是一个Function对象,我们可以在里面定义监督指令,使用DeciderBuilder构建工厂可以快速编写,监督指令有:
下面,我们写一个示例来观察这几个指令,示例如下:
监控者SupervisorActor类:public class SupervisorActor extends AbstractActor {
/**
* 自定义OneForOneStrategy监督策略
*/
private static SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.ofMinutes(1),
DeciderBuilder.match(IOException.class, e -> SupervisorStrategy.resume())
.match(NullPointerException.class, e -> SupervisorStrategy.restart())
.match(IllegalArgumentException.class, e -> SupervisorStrategy.stop())
.matchAny(o -> SupervisorStrategy.escalate())
.build());
private final LoggingAdapter logger = Logging.getLogger(getContext().getSystem(), this);
public static void main(String[] args) throws InterruptedException {
ActorSystem system = ActorSystem.create("system");
ActorRef supervisor = system.actorOf(Props.create(SupervisorActor.class), "supervisor");
supervisor.tell("IOException", ActorRef.noSender());
//supervisor.tell("NullPointerException", ActorRef.noSender());
//supervisor.tell("IllegalArgumentException", ActorRef.noSender());
supervisor.tell("get", ActorRef.noSender());
}
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
@Override
public void preStart() throws Exception {
ActorRef childActor = getContext().actorOf(Props.create(ChildActor.class), "childActor");
//监控
getContext().watch(childActor);
}
@Override
public Receive createReceive() {
return receiveBuilder().match(String.class, s -> {
Option childActor = getContext().child("childActor");
ActorRef child = childActor.get();
if ("IOException".equals(s)) {
child.tell(new IOException(), getSelf());
} else if ("NullPointerException".equals(s)) {
child.tell(new NullPointerException("空指针异常"), getSelf());
} else if ("IllegalArgumentException".equals(s)) {
child.tell(new IllegalArgumentException(), getSelf());
}else if("get".equals(s)){
child.tell("get", getSelf());
}
}).match(Terminated.class, t -> {
logger.info("监控到" + t.getActor() + "停止了");
}).matchAny(other -> {
logger.info("state= " + other);
}).build();
}
}
子类ChildActor ,我们重写preStart、postStop、preRestart、postRestart四个方法,方便我们观察不同指令下的流程:public class ChildActor extends AbstractActor {
/**
* 用来观察不同处理后的结果
*/
private int state = 1;
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Exception.class, e -> {
this.state++;
throw e;
})
.matchEquals("get", s -> {
this.state++;
getSender().tell(state, getSelf());
})
.matchAny(o -> {
this.state++;
unhandled(o);
})
.build();
}
@Override
public void preStart() throws Exception {
super.preStart();
System.out.println("child preStart");
}
@Override
public void postStop() throws Exception {
super.postStop();
System.out.println("child postStop");
}
@Override
public void preRestart(Throwable reason, Optional message) throws Exception {
System.out.println("child preRestart start: " + this.state);
super.preRestart(reason, message);
System.out.println("child preRestart end: " + this.state);
}
@Override
public void postRestart(Throwable reason) throws Exception {
System.out.println("child postRestart start: " + this.state);
super.postRestart(reason);
System.out.println("child postRestart end: " + this.state);
}
}
这里特别提醒一下,ChildActor的createReceive方法中的this.state++必须写在每个匹配项里,如若写在createReceive方法里,只会执行一次。
运行SupervisorActor 的main方法,测试三种异常带来的不同状态结果(运行结果不贴出来了,大家可以运行一下示例), 结果说明如下:
正确的使用监控和容错机制,可以在程序出现异常或故障时,保证系统不崩溃,对于系统的高可用和稳定性提供极大的保障。