《Java高并发程序设计》学习 --7.4 Akka之监督策略

45 篇文章 0 订阅
Akka框架赋予了足够的控制权。在Akka框架内,父Actor可以对子Actor进行监督,监控Actor的行为是否有异常。大体上,监督策略可以分为两种:一种是OneForOneStrategy的监督,另一种是AllForOneStrategy。
对于OneForOneStrategy的策略,父Actor只会对出问题的子Actor进行处理,比如重启或者停止,而对于AllForOneStrategy,父Actor会对出问题的子Actor以及它所有的兄弟都进行处理。对于AllForOneStrategy策略,它更加适合于各个Actor联系非常紧密的场景,如果多个Actor间只要有一个Actor出现故障,则宣告整个任务失败,就比较适合使用AllForOneStrategy,否则,在更多场景中,应该使用OneForOneStrategy。OneForOneStrategy也是Akka的默认策略。
在一个指定的策略中,可以对Actor的失败情况进行相应的处理,比如:当失败时,可以无视这个错误,继续执行Actor,就像什么事都没发生过一样。或者可以重启这个Actor,甚至可以让这个Actor彻底停止工作。要指定这些监督行为,只要构造一个自定义的监督策略即可。
下面简单看一下SupervisorStrategy的使用和设置。首先,需要定义一个父Actor,它作为所有子Actor的监督者:
public class Supervisor extends UntypedActor {
	private static SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create(1,TimeUnit.MINUTES), 
		new Function<Throwable, Directive>() {
			@Override
			public Directive apply(Throwable t) throws Exception {
				if(t instanceof ArithmeticException) {
					System.out.println("meet ArithmeticException,just resume");
					return SupervisorStrategy.resume();
				} else if(t instanceof NullPointerException) {
					System.out.println("meeting NullPointerException,restart");
					return SupervisorStrategy.restart();
				} else if(t instanceof IllegalArgumentException) {
					return SupervisorStrategy.stop();
				} else {
					return SupervisorStrategy.escalate();
				}
			}
		});


	public SupervisorStrategy supervisorStrategy() {
		return strategy;
	};

	public void onReceive(Object o) {
		if(o instanceof Props) {
			getContext().actorOf((Props)o,"restartActor");
		} else {
			unhandled(o);
		}
	}
}
上述代码中,定义了一个OneForOneStrategy的监督策略。在这个监督策略中,运行Actor在遇到错误后,在1分钟内进行3次重试。如果超过这个频率,那么就会直接杀死Actor。具体的策略由第5~16行定义。这里的含义是,当遇到ArithmeticException异常时(比如除以0的错误),继续指定这个Actor,不做任何处理(第8行);当遇到空指针时,进行Actor的重启(第11行)。如果遇到IllegalArgumentException异常,则直接停止Actor。对于在这个函数中没有涉及的异常,则向上抛出,由更顶层的Actor处理。
第20~23行覆盖父类的supervisorStrategy()方法,设置使用自定义的监督策略。
第27行用来新建一个名为restartActor的子Actor,这个字Actor就由当前的Supervisor进行监督。当Supervisor接收下一个Props对象时,就会根据这个Props配置生成一个restartActor。
RestartActor的实现如下:
public class RestartActor extends UntypedActor {
	public enum Msg {
		DONE,RESTART
	}
	
	@Override
	public void preStart() throws Exception {
		System.out.println("preStart hashcode:" + this.hashCode());
	}
	
	@Override
	public void postStop() throws Exception {
		System.out.println("postStop hashcode:" + this.hashCode());
	}
	
	@Override
	public void postRestart(Throwable reason) throws Exception {
		super.postRestart(reason);
		System.out.println("postRestart hashcode:" + this.hashCode());
	}
	
	@Override
	public void preRestart(Throwable reason, Option<Object> message)
			throws Exception {
		System.out.println("preRestart hashcode:" + this.hashCode());
	}
	
	@Override
	public void onReceive(Object msg) throws Exception {
		if(msg == Msg.DONE) {
			getContext().stop(getSelf());
		} else if(msg == Msg.RESTART) {
			System.out.println(((Object)null).toString());
			double a = 0/0;
		}
		unhandled(msg);
	}
}
第6~25行,定义了一些Actor的声明周期的回调接口。目的是更好地观察Actor的活动情况。在第32~34行模拟了一些异常情况,第32行会抛出NullPointerException,而第34行因为除以0,所以会抛出IllegalArgumentException。
主函数如下定义:
public static void customStrategy(ActorSystem system) {
	ActorRef a = system.actorOf(Props.create(Supervisor.class),"Supervisor");
	a.tell(Props.create(RestartActor.class), ActorRef.noSender());

	ActorSelection sel = system.actorSelection("akka://lifecycle/user/Supervisor/restartActor");
	for(int i=0; i<100; i++) {
		sel.tell(RestartActor.Msg.RESTART, ActorRef.noSender());
	}
}
	
public static void main(String[] args) {
	ActorSystem system = ActorSystem.create("lifecycle",        ConfigFactory.load("lifecycle.conf"));
	customStrategy(system);
}
上述代码中,第12行低吗创建了全局ActorSystem,接着customStrategy()函数中创建了Supervisor Actor,并且对Supervisor发送一个RestartActor的Props(第3行,这个消息会使Supervisor创建RestartActor)。
接着,选中RestartActor实例(第5行)。第7~9行,向这个RestartActor发送100条RESTART消息。这会使得RestartActor抛出NullPointerException。
执行上述代码,部分输出如下:
preStart hashcode:1572178076
meeting NullPointerException,restart
preRestart hashcode:1572178076
preStart hashcode:536104499
postRestart hashcode:536104499
meeting NullPointerException,restart
preRestart hashcode:536104499
preStart hashcode:1497261089
postRestart hashcode:1497261089
meeting NullPointerException,restart
preRestart hashcode:1497261089
preStart hashcode:253747277
postRestart hashcode:253747277
meeting NullPointerException,restart
postStop hashcode:253747277
[ERROR] [01/12/2017 21:52:14.041] [lifecycle-akka.actor.default-dispatcher-3] [akka://lifecycle/user/Supervisor/restartActor] null
java.lang.NullPointerException
	at cn.guet.parallel.akka.RestartActor.onReceive(RestartActor.java:39)
	at akka.actor.UntypedActor$$anonfun$receive$1.applyOrElse(UntypedActor.scala:167)
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)
	at akka.actor.ActorCell.invoke(ActorCell.scala:456)
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)
	at akka.dispatch.Mailbox.run(Mailbox.scala:219)
	at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:385)
	at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
	at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
	at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
	at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
第1行的preStart表示RestartActor正在初始化,注意它的HashCode为1572178076。接着,这个Actor遇到了NullPointerException。根据自定义的策略,这将导致它重启,因此,这就有了第3行的preRestart,因为preRestart在正式重启之前调用,因此HashCode还是1572178076,表示当前Actor和上一个Actor还是同一个实例。接着,第4行进入preStart()方法,它的HashCode为536104499。这说明系统已经为这个RestartActor生成了一个新的实例,原有的实例因为重启而被回收。新的实例将代替原有实例继续工作。这说明同一个RestartActor在系统的工作始终,未必能保持同一个实例。重启完成后,调用postRestart()方法。实际上,Actor重启后的preStart()方法,就是在postRestart()中调用的(Action父类的postRestart()会调用preStart()方法)。

注:本篇博客内容总结自《 Java 高并发程序设计》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值