Actor 简易教程

来源:互联网 发布:怎么使用 java工作流 编辑:程序博客网 时间:2024/05/02 00:59

Akka Actor 学习

Actor System

每个Actor 独立运行,Actor间只有通过消息传递来communication.- ! 传递消息 并立即返回- ? 传递消息 返回一个变量future用来表示可能的回复.

每个Actor 有一个mailbox来接受信息, 默认的接受方式是FIFO.

Akka框架保证每个Actor的实例拥有一个轻量级线程, 并保证每次处理一个消息.

用户可以通过Akka Acotr API 获取以下信息:- sender 当前处理消息的发送Actor.- context 获取当前Actor的关于context的信息和方法- supervisionStrategy 使用此类来定义错误恢复策略- self 当前Actor本身

一下通过一个count文本中word数的例子来讲解Actor的使用方法:首先将problem分解为两个子任务, 1. 子任务count每行的word数; 2. 父任务累加每个子任务的count和.

父任务从文本读取并将每行发送给一个子任务, 子任务收到一行后计算word量并发送count给父任务.

case class ProcessStringMsg(string: String)case class StringProcessedMsg(words: Integer)class StringCounterActor extends Actor {  def receive = {    case ProcessStringMsg(string) => {      val wordsInLine = string.split(" ").length      sender ! StringProcessedMsg(wordsInLine)    }    case _ => println("Error: message not recognized")  }}

子任务actor任务很简单,消耗一个ProcessStringMsg 从中解压出字符串然后统计字符串的word个数, 再使用 ! 将wordsInLine回传给sender.

1.  case class StartProcessFileMsg()2.3.  class WordCounterActor(filename: String) extends Actor {4.  5.    private var running = false6.    private var totalLines = 07.    private var linesProcessed = 08.    private var result = 09.    private var fileSender: Option[ActorRef] = None10.  11.   def receive = {12.     case StartProcessFileMsg() => {13.       if (running) {14.         // println just used for example purposes;15.         // Akka logger should be used instead16.         println("Warning: duplicate start message received")17.       } else {18.         running = true19.         fileSender = Some(sender) // save reference to process invoker20.         import scala.io.Source._21.         fromFile(filename).getLines.foreach { line =>22.           context.actorOf(Props[StringCounterActor]) ! ProcessStringMsg(line)23.           totalLines += 124.         }25.       }26.     }27.     case StringProcessedMsg(words) => {28.       result += words29.       linesProcessed += 130.       if (linesProcessed == totalLines) {31.         fileSender.map(_ ! result)  // provide result to process invoker32.       }33.     }34.     case _ => println("message not recognized!")35.   }36. }

父任务收到StartProcessFileMsg后读取file并对每行开启一个Actor, 将该行发送给该Actor. 收到StringProcessedMsg后累加到总count中, 收集到所有子任务的结果后将总count回传到开启发送StartProcessFileMsg的Actor.

使用一下代码来开启整个app.

object Sample extends App {  import akka.util.Timeout  import scala.concurrent.duration._  import akka.pattern.ask  import akka.dispatch.ExecutionContexts._  implicit val ec = global  override def main(args: Array[String]) {    val system = ActorSystem("System")    val actor = system.actorOf(Props(new WordCounterActor(args(0))))    implicit val timeout = Timeout(25 seconds)    val future = actor ? StartProcessFileMsg()    future.map { result =>      println("Total number of words " + result)      system.shutdown    }  }}

使用 ? 来获得回传的消息.

错误恢复

再actor 系统中,每个actor都是其子actor的监护人.也就是说如果一个actor遇到了错误, 它会暂停自己和它所有的子actor.Actor处理又其孩子传上来的exceptions所使用的策略叫做监护人策略.当错误信息到达监护人时,可以采取一下几种策略:- 恢复子actor以及actor的子actor, 保持它的内部状态.- 重启子actor, 清空它的内部状态.- 永久停止子actor.- 停止自己, 并将错误信息往上传.

actor可以决定将策略应用到错误的子actor上还是应用到所有子actor上.- OneForOneStrategy: 只对错误的子actor生效.- AllForOneStrategy: 对所有子actor生效.

以下是一个例子:

import akka.actor.OneForOneStrategyimport akka.actor.SupervisorStrategy._import scala.concurrent.duration._override val supervisorStrategy = OneForOneStrategy() {   case _: ArithmeticException      => Resume   case _: NullPointerException     => Restart   case _: IllegalArgumentException => Stop   case _: Exception                => Escalate }

位置透明

Akka 体系支持位置透明, 使得actor不需在意消息发送者来自哪里. 消息发送者跟消息接受者既可以在同一个JVM/节点, 也可以不在同一个JVM/节点上. 当在不同的节点上时, 消息必须继承Serializable.程序员唯一需要做的就是配置application.conf文件, 以下是这个文件的一个示例:

akka {  actor {    provider = "akka.remote.RemoteActorRefProvider"  }  remote {    transport = "akka.remote.netty.NettyRemoteTransport"    netty {      hostname = "127.0.0.1"      port = 2552    }  }}

需要注意的是, 尽可能的将消息定义为imutable, 以防止出现不可以预计的错误.

0 0
原创粉丝点击