Akka 使用系列之三: 层次结构

开发 开发工具
我们使用 Akka 开发并行程序时,可以使用层级结构组织 Actors。层次结构不仅比较符合人类直觉,还为容错提供了机制保障。

Akka 是用层次结构组织 Actors 的。

Akka

1. Akka 的层次结构

我们需要实现一个翻译模块,其功能是输入中文输出多国语言。我们可以让一个 Master Actor 负责接收外界输入,多个 Worker Actor 负责将输入翻译成特定语言,Master Actor 和 Worker Actor 之间是上下级层次关系。下图展示了这种层级结构。

具体代码实现如下所示。

  1. class Master extends Actor with ActorLogging{ 
  2.     val english2chinese  
  3.     = context.actorOf(Props[English2Chinese],"English2Chinese") 
  4.     val english2cat      
  5.     = context.actorOf(Props[English2Cat],"English2Cat") 
  6.  
  7.     def receive = { 
  8.         case eng1:String =>
  9.             english2chinese ! eng1 
  10.             english2cat     ! eng1 
  11.         } 
  12.     } 
  13.  
  14. class English2Chinese extends Actor with ActorLogging{ 
  15.     def receive = { 
  16.         case eng:String => { 
  17.             println("我翻译不出来!") 
  18.         } 
  19.     } 
  20.  
  21. class English2Cat extends Actor with ActorLogging{ 
  22.     def receive = { 
  23.         case eng:String =>
  24.              println( "喵喵喵!") 
  25.         } 
  26.     } 
  27.  
  28. object Main{ 
  29.     def main(args:Array[String])={ 
  30.         val sys = ActorSystem("system") 
  31.         val master = sys.actorOf(Props[Master],"Master") 
  32.         master ! "Hello,world!" 
  33.     } 

我们在 Master Actor 中使用 context.actorOf 实例化 English2Chinese 和 English2Cat,便可以在它们之间形成层次关系。这点通过它们的 actor 地址得到证实。

上面的 Actors 层次结构是我们程序里 Actor 的层次结构。这个层次结构是 Actor System 层次结构的一部分。Actor System 层次结构从根节点出来有两个子节点:UserGuardian 和 SystemGuardian。用户程序产生的所有 Actor 都在 UserGuardian 节点下,SystemGuardian 节点则包含系统中的一些 Actor,比如 deadLetterListener。如果一个 Actor 已经 stop 了,发送给这个 Actor 的消息就会被转送到 deadLetterListener。因此完整的 Actor 层次结构如下所示。

完整的 Actor 层次结构

2. Akka 的容错机制

对于分布式系统来说,容错机制是很重要的指标。那么 Akka 是怎么实现容错的呢?Akka 的容错机制是基于层次结构: Akka 在 Actor 加一个监控策略,对其子 Actor 进行监控。下面的代码是给 Actor 加了一个监控策略,其监控策略内容:如果子 Actor 在运行过程中抛出 Exception,对该子 Actor 执行停止动作 (即停止该子 Actor)。

  1. override val supervisorStrategy  
  2. OneForOneStrategy(){ 
  3.     case _:Exception => Stop 

Akka 的监控策略一共支持四种动作:Stop, Resume, Restart 和 Escalate。

  1. Stop:子 Actor 停止。
  2. Resume:子 Actor 忽略引发异常的消息,继续处理后续消息。
  3. Restart:子 Actor 停止,重新初始化一个子 Actor 处理后续消息
  4. Escalate:错误太严重,自己已经无法处理,将错误信息上报给父 Actor。

Akka 的监控策略分为两种。一种是 OneForOne。这种策略只对抛出 Exception 的子 Actor 执行相应动作。还是拿上面的翻译模块做例子,我们加入一个 OneForOne 的 Stop 的监控策略。

  1. class Master1 extends Actor with ActorLogging{ 
  2.   val english2Chinese=   
  3.   context.actorOf(Props[English2Chinese1],"English2Chinese") 
  4.   val english2Cat =  
  5.   context.actorOf(Props[English2Cat1], "English2Cat") 
  6.  
  7.   override val supervisorStrategy  
  8.   = OneForOneStrategy(){ 
  9.     case _:Exception => Stop 
  10.   } 
  11.  
  12.   override def receive = { 
  13.     case eng:String => { 
  14.       english2Cat ! eng; 
  15.       english2Chinese ! eng; 
  16.      } 
  17.   } 
  18.  
  19. class English2Chinese1 extends Actor with ActorLogging{ 
  20.   override def receive = { 
  21.     case eng:String => { 
  22.       println("翻译不出来") 
  23.     } 
  24.   } 
  25.  
  26. class English2Cat1 extends Actor with ActorLogging{ 
  27.   override def receive = { 
  28.     case eng:String => { 
  29.       throw new Exception("Exception in English2Cat1") 
  30.     } 
  31.   } 
  32.  
  33. object hierarchy1 { 
  34.   def main(args:Array[String])={ 
  35.     val system  
  36.     = ActorSystem("system") 
  37.     val master 
  38.     = system.actorOf(Props[Master1],"Master") 
  39.  
  40.     master ! "Hello, world" 
  41.     Thread.sleep(1000) 
  42.     master ! "Hello, world" 
  43.   } 

运行这段代码,我们得到下面结果。从下面的结果,我们可以看出:***轮 English2Cat1 抛出了 Exception, English2Chinese1 正常工作;第二轮,English2Cat1 已经死了,English2Chinese1 也已经死亡了。这个结果说明监控策略已经将 MasterActor 的所有子 Actor 停止了。

另一种是 AllForOne。如果有子 Actor 抛出 Exception,这种监控策略对所有子 Actor 执行动作。

  1. class Master2 extends Actor with ActorLogging{ 
  2.   val english2Chinese  
  3.   = context.actorOf(Props[English2Chinese2],"English2Chinese") 
  4.   val english2Cat      
  5.   = context.actorOf(Props[English2Cat2], "English2Cat") 
  6.  
  7.   override val supervisorStrategyAllForOneStrategy() { 
  8.     case _: Exception => Stop 
  9.   } 
  10.  
  11.   override def receive = { 
  12.     case eng:String => { 
  13.       english2Cat ! eng; 
  14.       english2Chinese ! eng; 
  15.      } 
  16.   } 

运行这段代码,我们得到下面结果。从下面的结果,我们可以看出:***轮 English2Cat1 抛出了 Exception, English2Chinese1 正常工作;第二轮,English2Cat1 已经死了,English2Chinese1 也已经死亡了。这个结果说明监控策略已经将 MasterActor 的所有子 Actor 停止了。

3. 总结

 

我们使用 Akka 开发并行程序时,可以使用层级结构组织 Actors。层次结构不仅比较符合人类直觉,还为容错提供了机制保障。

【本文为51CTO专栏作者“李立”的原创稿件,转载请通过51CTO获取联系和授权】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2017-06-15 13:29:12

AkkaSpark异步

2011-06-14 10:35:15

性能优化

2018-04-17 15:26:44

物联网发展趋势互联网

2009-06-02 14:28:14

品牌故事APC

2011-11-03 11:42:42

虚拟化vmwareVMware View

2010-07-09 09:37:00

HART协议

2009-09-05 13:02:17

IT职业规划孔文达

2009-09-22 13:11:01

ibmdwSOA

2015-10-30 15:30:54

LevelDBSSTableSybase

2010-07-06 10:19:15

SQL Server层

2013-12-02 14:13:54

jQueryUI

2022-07-13 15:54:14

Matplotlib图表

2019-07-30 12:36:10

云计算微软亚马逊

2021-02-22 14:04:47

Vue框架项目

2021-02-04 07:22:07

NPOI操作Excel

2019-09-28 23:17:41

zabbix运维监控

2012-03-15 17:18:33

JavaHashMap

2012-02-15 10:37:38

JavaJava Socket

2011-06-24 16:26:20

SEO

2010-06-12 16:47:11

网络层次协议
点赞
收藏

51CTO技术栈公众号