[转自: http://blog.csdn.net/zjw10wei321/article/details/46911825]

容错

正如角色系统所描述的,每个actor都是其子actor的监管者,并且每个actor都定义了故障处理监管策略。这个策略作为角色系统结构的一部分,一经创建后就不能再修改。

实际中的故障处理

首先我们看一个在实际应用中典型故障的案例,演示处理数据存储错误的一种方法。当然这取决于实际的应用中,当存储数据失败时可以做些什么,但在本例中我们使用尽量重新连接的方法。 
阅读下面的源代码。代码内部的注释解释了各个片段的故障处理以及为什么添加它们。强烈推荐运行这个示例,因为很容易就顺着日志的输出,理解在运行时发生了什么。

容错案例图解

 
上图阐述了正常消息流。 
常规流程:

步骤 描述
1 Listener开始干活。
2 Worker定期的向自己发送Do信息来安排工作。
3,4,5 当Worker收到Do信息后,向CounterService发送3条Increment信息去增加计数器。CounterService将Increment信息转发给Counter,由Counter更新计数器变量,并将计数器当前值发送给Storage。
6,7 Worker 请求CounterService获取当前计数器的值,并将结果返回给Listener。

上图阐述了在存储失败时发生了什么 
失败流程:

步骤 描述
1 Storage抛出StorageException。
2 CounterService是Storage的监管者,当StorageException被抛出时并重启Storage。
3,4,5,6 Storage继续失败,继续被重启。
7 Storage在5秒内经历3次失败和重启后,会被其监管者(CounterService)停止。
8 CounterService同样在监视Storage为其终止,在当Storage终止时,接收到Terminated消息。
9,10,11 CounterService这时告诉Counter没有Storage。
12 CounterService计划发送一个Reconnect消息给自己。
13,14 当CounterService接受到Reconnect消息后创建一个新的Storage。
15,16 CounterService告诉Counter用这个新的Storage。

容错案例所有源码

这里就不列出了,详细请看我的github

创建新的监管策略

下面的章节将说明故障处理的原理以及更深层的替代。 
为了达到示范的效果,我们假使有如下的策略:

private static SupervisorStrategy strategy =
new OneForOneStrategy(10, Duration.create("1 minute"),
new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable t) {
if (t instanceof ArithmeticException) {
return resume();
} else if (t instanceof NullPointerException) {
return restart();
} else if (t instanceof IllegalArgumentException) {
return stop();
} else {
return escalate();
}
}
}); @Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}

我选择了几个著名的异常类型演示应用程序中被描述成故障处理指令在监管和监测中的使用。首先,一对一的策略,意味着每一个子actor会被单独的治愈(多对一策略与之相似,唯一不同的是这个策略是针对监管者所有的子actor而不仅仅是出故障的那一个)。这里限制重启的频率,最大程度上每分钟重启10次。-1 和Duration.Inf()的限制并不适用,可以指定一个重启绝对上限或者让重启无限。超过了期限后子actor会被停止。

注意: 
如果这个策略是在监管者角色内部声明的(而不是一个单独的类),决策者可以在线程安全样式下访问角色内部的所有状态,包含获取失败子节点的引用(故障信息中getSender有效)。

默认监管策略

如果定义过的策略没有覆盖到被抛出的异常,Escalate (逐步上升,丢给父监管者)志在必行。 
当actor中没有定义监管策略,如下的异常将会被默认处理掉:

  • ActorInitializationException 会停止掉失败了的子actor。
  • ActorKilledException 会停止掉失败了的子actor。
  • Exception 会重启失败了的子actor。
  • 其他类型的Throwable 将会上升到父actor。

如果异常被上升一路达到根监护者那,在那也会用上述默认策略方式处理掉。

停止监管策略

跟Erlang方式类似的策略是当它们失败的时候只停止子actor,以及当DeathWatch通知丢失的子actor的时候会对监管者采取纠正的动作。

记录actor失败的消息

默认SupervisorStrategy会记录失败信息除非它们会被逐级上升。在高层次机构中处理被逐级上升的错误,并潜在的记录下来。 
在初始化的时候你可以通过设置SupervisorStrategy的loggingEnabled为false用来不激活默认的日志。 在Decider内部可以定制日志。注意获取当前失败的子acotrRef是有效的,当SupervisorStrategy在监管角色中描述,如同getSender一样。 
你可以自定义化日志通过实现SupervisorStrategy重写logFailure方法。

顶层角色的监管

顶层角色意味着是用system.actorOf()创建的,是用户监护人的孩子。 
在这种情况下没有特殊规则应用,监护只适用于配置的策略。

应用测试

下面章节展示了在实际中不同指令的效果,因此咱们需要一个测试环境。首先需要一个合适有效的监管者:

public class Supervisor extends UntypedActor {

  private static SupervisorStrategy strategy =
new OneForOneStrategy(10, Duration.create("1 minute"),
new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable t) {
if (t instanceof ArithmeticException) {
return resume();
} else if (t instanceof NullPointerException) {
return restart();
} else if (t instanceof IllegalArgumentException) {
return stop();
} else {
return escalate();
}
}
}); @Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
} public void onReceive(Object o) {
if (o instanceof Props) {
getSender().tell(getContext().actorOf((Props) o), getSelf());
} else {
unhandled(o);
}
}
}

此监管者将会创建一个子actor,好让我们可以作如下试验:

public class Child extends UntypedActor {
int state = 0; public void onReceive(Object o) throws Exception {
if (o instanceof Exception) {
throw (Exception) o;
} else if (o instanceof Integer) {
state = (Integer) o;
} else if (o.equals("get")) {
getSender().tell(state, getSelf());
} else {
unhandled(o);
}
}
}

在测试actor系统中测试使用工具更容易简化,TestProbe提供了有用的角色引用,用来接收和检查消息的回复。

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.SupervisorStrategy;
import static akka.actor.SupervisorStrategy.resume;
import static akka.actor.SupervisorStrategy.restart;
import static akka.actor.SupervisorStrategy.stop;
import static akka.actor.SupervisorStrategy.escalate;
import akka.actor.SupervisorStrategy.Directive;
import akka.actor.OneForOneStrategy;
import akka.actor.Props;
import akka.actor.Terminated;
import akka.actor.UntypedActor;
import scala.collection.immutable.Seq;
import scala.concurrent.Await;
import static akka.pattern.Patterns.ask;
import scala.concurrent.duration.Duration;
import akka.testkit.TestProbe; public class FaultHandlingTest {
static ActorSystem system;
Duration timeout = Duration.create(5, SECONDS); @BeforeClass
public static void start() {
system = ActorSystem.create("test");
} @AfterClass
public static void cleanup() {
JavaTestKit.shutdownActorSystem(system);
system = null;
} @Test
public void mustEmploySupervisorStrategy() throws Exception {
// code here
} }

创建角色:

Props superprops = Props.create(Supervisor.class);
ActorRef supervisor = system.actorOf(superprops, "supervisor");
ActorRef child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);

第一个测试将演示resume指令,因此我们尝试在actor中设置一些非初始化的状态,并且让actor出现故障:

child.tell(42, ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(42);
child.tell(new ArithmeticException(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(42);

你可以看到在错误处理指令完后仍能得到42。现在我们将故障换成更严重的NullPointerException,那将不再是这样的情况:

child.tell(new NullPointerException(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(0);

最后看看最致命的IllegalArgumentException,监管者会终止其子actor:

final TestProbe probe = new TestProbe(system);
probe.watch(child);
child.tell(new IllegalArgumentException(), ActorRef.noSender());
probe.expectMsgClass(Terminated.class);

到目前为止,监管者完全不受子actor故障的影响,因为指令集会处理掉。在Exception情况下,就不会是上述情况了,监管者会将失败情况逐级上升传递:

child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
probe.watch(child);
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
child.tell(new Exception(), ActorRef.noSender());
probe.expectMsgClass(Terminated.class);

监管者它自己会被ActorSystem提供的顶层actor监管,顶层actor对所有异常(ActorInitializationException 和ActorKilledException异常是例外)使用默认故障策略去重启。因为默认指令的重启是杀死所有子actor,我们预期到这些脆弱的子actor是不会在故障中幸存。

假如这不是所期望的(依赖于实际用例),我们需要使用一个不同的监管者来覆盖它的方法。

public class Supervisor2 extends UntypedActor {

  private static SupervisorStrategy strategy = new OneForOneStrategy(10,
Duration.create("1 minute"),
new Function<Throwable, Directive>() {
@Override
public Directive apply(Throwable t) {
if (t instanceof ArithmeticException) {
return resume();
} else if (t instanceof NullPointerException) {
return restart();
} else if (t instanceof IllegalArgumentException) {
return stop();
} else {
return escalate();
}
}
}); @Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
} public void onReceive(Object o) {
if (o instanceof Props) {
getSender().tell(getContext().actorOf((Props) o), getSelf());
} else {
unhandled(o);
}
} @Override
public void preRestart(Throwable cause, Option<Object> msg) {
// do not kill all children, which is the default here
}
}

在这个父actor下,子actor会逐步上升中的重启而幸存,如下的最后测试:

superprops = Props.create(Supervisor2.class);
supervisor = system.actorOf(superprops);
child = (ActorRef) Await.result(ask(supervisor,
Props.create(Child.class), 5000), timeout);
child.tell(23, ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(23);
child.tell(new Exception(), ActorRef.noSender());
assert Await.result(ask(child, "get", 5000), timeout).equals(0);
 
 

Akka Java 文档 -- 容错的更多相关文章

  1. JAVA文档注释标签

    1 常用Java注释标签(Java comment tags) @author  作者 @param  输入参数的名称  说明 @return 输出参数说明 @since JDK版本 @version ...

  2. JAVA基础学习之命令行方式、配置环境变量、进制的基本转换、排序法、JAVA文档生成等(1)

    1.命令行方式 dos命令行,常见的命令: dir:列出当前目录下的文件以及文件夹 md:创建目录 rd:删除目录 cd:进入指定目录 cd..:退回到上一级目录 cd/:退回到根目录 del:删除文 ...

  3. JAVA 文档注释,类的说明,HTML说明文档的生成

    有的时候,我们会写一些类,编译成.class文件,给别人使用,那么,别人不知道这个类有哪些方法,如何调用. 所以我们需要做一个类的说明文档. 可以采用在.java类里面进行注释,通过注释来生成类的说明 ...

  4. Java - 34 Java 文档注释

    Java 文档注释 Java只是三种注释方式.前两种分别是// 和/* */,第三种被称作说明注释,它以/** 开始,以 */结束. 说明注释允许你在程序中嵌入关于程序的信息.你可以使用javadoc ...

  5. Java-Runoob-高级教程:Java 文档注释

    ylbtech-Java-Runoob-高级教程:Java 文档注释 1.返回顶部 1. Java 文档注释 Java 支持三种注释方式.前两种分别是 // 和 /* */,第三种被称作说明注释,它以 ...

  6. Java 学习(20):Java Applet 基础 & Java 文档注释

    -- Java Applet 基础 -- Java 文档注释 Java Applet 基础 Applet 是一种 Java 程序.它一般运行在支持 Java 的 Web 浏览器内.因为它有完整的 Ja ...

  7. Java文档查看

    对于Java学习者来说,阅读Java文档是必不可少的步骤,比如我现在想知道List接口的retianAll()方法,该怎么办呢? 当然是百度了!!! 皮一下,当然是查找Java文档了,以JDK1.7版 ...

  8. 如何为我们的程序编写开发文档——Java文档注释

    Java文档注释是用于生成Java API文档的注释,通过在程序中的类.属性.方法部分加上注释,就可以用javadoc命令生成漂亮的API文档,是程序员进阶的必备技能. 注意,文档注释只说明紧跟其后的 ...

  9. java基础课程笔记 static 主函数 静态工具类 classpath java文档注释 静态代码块 对象初始化过程 设计模式 继承 子父类中的函数 继承中的构造函数 对象转型 多态 封装 抽象类 final 接口 包 jar包

    Static那些事儿 Static关键字 被static修饰的变量成为静态变量(类变量) 作用:是一个修饰符,用于修饰成员(成员变量,成员方法) 1.被static修饰后的成员变量只有一份 2.当成员 ...

随机推荐

  1. Python--day38--多进程的方法属性总结

    多进程的方法属性:

  2. 2019年第二阶段我要变强个人训练赛第八场 B.序列(seq)

    传送门 B.序列(seq) •题目描述 给出一个长度为n的序列a,每次对序列进行一下的某一个操作. •输入 第一行两个整数n,q表示序列长度和操作个数. 接下来一行n个数,表示序列a. 接下来q行表示 ...

  3. 【踩坑记录】vue单个组件内<style lang="stylus" type="text/stylus" scoped>部分渲染失效

    vue组件化应用,近期写的单个组件里有一个的渲染部分样式渲染不上去 因为同结构的其他组件均没有问题,所以排除是.vue文件结构的问题,应该是<style>内部的问题 <style l ...

  4. C# 如何写 DEBUG 输出

    本文来告诉大家一个规范,如何去写 DEBUG 的输出. 经常在代码中,需要使用 DEBUG 来输出一些奇怪的东西来进行测试.但是输出的窗口只有一个,如果有一个逗比在不停输出,那么就会让输出窗口看不到自 ...

  5. 【49.23%】【hdu 1828】Picture

    Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...

  6. 【Linux】centos查看防火墙是否关闭

    查看防火墙的状态的命令为: sudo systemctl status firewalld 打开防火墙的方式有两种,一种是打开后重启会恢复回原来的状态,命令为: sudo systemctl star ...

  7. 理解C/C++的复杂声明

      理解C/C++的复杂声明        曾经碰到过让你迷惑不解.类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复C/C ...

  8. sybase的存储过程编写经验和方法

    1.如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo.table_name”,因为sp_depends不能显示出该SP所使用的跨 ...

  9. Visual Studio 2019 编译.Net Core Console项目出现【MSB4018 The "CreateAppHost" task failed unexpectedly】 错误

    需要测试一个小东东,使用Visual Studio 2019新建了一个.Net Core的Console程序,但是在编译的时候一直报错,死活编译不通过. 错误信息: Severity Code Des ...

  10. xcode无线调试

    前言: xcode9 以上才会有无线调试这个功能,换了一个type-c口的mac,公司的新电脑,但是公司不给配转接口,到某东看了一下,type-c口同时可以转化usb和VGA的要198,官网差不多50 ...