转载一些博客内容:http://my.oschina.net/clopopo/blog/141886
要想写一个能正确并发、容错性好的可扩展应用是非常非常困难的。最主要的原因是我们用错了工具和在错误的级别上进行抽象(actor模式的抽象级别要远高于共享内存模式)。akka正式为了应对这种情况而孕育而生。actor模型提高了抽象级别并且提供了一个更好的平台去构建正确的、并发的可扩展应用。akka采用的是一种称为“let it crash”(让它去死)模式去获得良好的容错性。SPARK框架就是采用akka作为它的消息驱动系统。
简单介绍下actor模型:
Actor之间通过发送消息来通信,消息的传送是异步的,通过一个邮件队列(mail queue)来处理消息。每个Actor是完全独立的,可以同时执行它们的操作。每一个Actor是一个计算实体,映射接收到的消息到以下动作:
以上三种动作并没有固定的顺序,可以并发地执行。Actor会根据接收到的消息进行不同的处理。
在一个Actor系统中,包含一个未处理的任务集,每一个任务由以下三个属性标识:
简单起见,可以把一个任务视为一个消息,在Actor之间传递包含以上三个属性的值的消息。
Actor模型有两种任务调度方式:基于线程的调度以及基于事件的调度;基于线程的调度缺点是非常明显的,由于线程数量受到操作系统的限制,把线程和Actor捆绑起来势必影响到系统中可以同时的Actor数量。而线程数量一多也会影响到系统资源占用以及调度,而在某些情况下大部分的Actor会处于空闲状态,而大量阻塞线程既是系统的负担,也是资源的浪费。现有的Actor Model一般都会使用基于事件的调度方式。
跟Actor模型比较类似的两个模型:
1. ACE reactor是通过注册/回调方式进行驱动的程序开发模式,先注册自己关注什么事件,然后反应堆就会在该事件发生时回调你。这实际上与actor 模型有些类似。
2. proactor模型可以认为是reactor模型的一种异步实现,reactor要求收到请求后同步分发的请求处理器上,而proactor允许异步处理
在角色模型里面,事件处理者actors接收消息,唯一事件处理者代理actorRef 发送消息,然后接收的消息 放入共享队列(角色模型里面称为信箱)这样发送的消息只针对角色与角色之间。也就是说每个actor都有自己的消息队列
AKKA简介:
AKKA的几个关键模块,这次主要描述下Actor
actor是以一种严格的层级结构方式被创建的。这有点类似一个文件系统,文件夹内又包含子文件夹,子文件夹又包含子子文件夹,以此类推。我们可以把这个文件系统看成一个文件树。每一个节点都只有唯一的一个访问路径。actor 系统也是如此,这个访问路径就是actor的路径。这点跟zookeeper很相似。
在 Actor 系统 中说过,监管描述的是actor之间的关系:监管者将任务委托给下属并对下属的失败状况进行响应。当一个下属出现了失败(i.e. 抛出一个异常),它自己会将自己和自己所有的下属挂起然后向自己的监管者发送一个提示失败的消息。取决于所监管的工作的性质和失败的性质,监管者可以有4种基本选择:
1.让下属继续执行,保持下属当前的内部状态
2.重启下属,清除下属的内部状态
3.永久地终止下属
4.将失败沿监管树向上传递
在SPARK框架中有大量的actor模型的应用,我们简单看下spark client类
object Client { def main(args: Array[String]) { ...... // Verify driverArgs.master is a valid url so that we can use it in ClientActor safely Master.toAkkaUrl(driverArgs.master, AkkaUtils.protocol(actorSystem)) actorSystem.actorOf(Props(classOf[ClientActor], driverArgs, conf)) actorSystem.awaitTermination() } }请看上面代码的红字,可以看出,核心实现是由ClientActor实现的。Client的Actor是akka.Actor的一个扩展。对于Actor,从它对recevie的override就可以看出它需要处理的消息,代码如下:
override def receiveWithLogging = { case SubmitDriverResponse(success, driverId, message) => println(message) if (success) pollAndReportStatus(driverId.get) else System.exit(-1) case KillDriverResponse(driverId, success, message) => println(message) if (success) pollAndReportStatus(driverId) else System.exit(-1) case DisassociatedEvent(_, remoteAddress, _) => println(s"Error connecting to master ${driverArgs.master} ($remoteAddress), exiting.") System.exit(-1) case AssociationErrorEvent(cause, _, remoteAddress, _, _) => println(s"Error connecting to master ${driverArgs.master} ($remoteAddress), exiting.") println(s"Cause was: $cause") System.exit(-1) }