本文已.Net语法为主,同时写有Scala及Java实现代码

严肃的说,演员是一个广泛的概念,作为外行人我对Actor 模型的定义:

  Actor是一个系统中参与者的虚拟人物,Actor与Actor之间是可以相互沟通,所有的沟通都是通过Message

比如说一个呼叫中心,数以百万计的客户可能会呼叫一个1-800的号码,并与许多可能的客户服务代表进行对话

向这样类型的交互可以使用演员建模

在Actor模型中,一切都是Actor,就想在面向对象编程(OOP)中一切都是"Object"一样.在OOP中,你需要使用类和对象进行建模,在Akka中,你可以使用Actor和Message进行建模

以下是Akka中基本例子

.Net
  1. using System;
  2. using Akka.Actor;
  3.  
  4. namespace ActorsSendingMessages
  5. {
  6. /// <summary>
  7. /// 在Akka.NET中Actor都继承于UntypedActor。/// </summary>
  8. public class BasicActor : UntypedActor
  9. {
  10. protected override void PreStart()
  11. {
  12.  
  13. }
  14.  
  15. protected override void PreRestart(Exception reason, object message)
  16. {
  17.  
  18. }
  19.  
  20. protected override void OnReceive(object message)
  21. {
  22. //handle messages here
  23. }
  24.  
  25. protected override void PostStop()
  26. {
  27.  
  28. }
  29.  
  30. protected override void PostRestart(Exception reason)
  31. {
  32.  
  33. }
  34. }
  35. }
Scala
  1. class BasicActor extends UntypedActor{
  2.  
  3. override def preStart(): Unit = super.preStart()
  4.  
  5. override def postStop(): Unit = super.postStop()
  6.  
  7. override def preRestart(reason: Throwable, message: Option[Any]): Unit = super.preRestart(reason, message)
  8.  
  9. override def postRestart(reason: Throwable): Unit = super.postRestart(reason)
  10.  
  11. override def onReceive(message: Any): Unit = {
  12.  
  13. }
  14.  
  15. }
Java
  1. import akka.actor.UntypedActor;
  2. import scala.Option;
  3.  
  4. /**
  5. * Created by Lei on 2017-4-25.
  6. */
  7. public class BasicActor extends UntypedActor {
  8. @Override
  9. public void onReceive(Object message) throws Throwable {
  10.  
  11. }
  12.  
  13. @Override
  14. public void preStart() throws Exception {
  15. super.preStart();
  16. }
  17.  
  18. @Override
  19. public void postStop() throws Exception {
  20. super.postStop();
  21. }
  22.  
  23. @Override
  24. public void preRestart(Throwable reason, Option<Object> message) throws Exception {
  25. super.preRestart(reason, message);
  26. }
  27.  
  28. @Override
  29. public void postRestart(Throwable reason) throws Exception {
  30. super.postRestart(reason);
  31. }
  32.  
  33. }

什么是Message

可能已经注意到了BasicActor中定义的方法

.Net
  1. protected override void OnReceive(object message)
  2. {
  3. //handle messages here
  4. }
Scala
  1. override def onReceive(message: Any): Unit = {
  2.  
  3. }
Java
  1. @Override
  2. public void onReceive(Object message) throws Throwable {
  3.  
  4. }

该OnReceive方法是Actor接收Message的地方,在Akka中,一个Message就是一个Object,Message可以是任意类型的实例

Actor通常只能处理特定类型的Message,如果Actor收到无法处理的Message,则不会触发任何异常,一般只是将Message标记为"unhandled"
Message是不变的

什么是"不可变"的对象呢?

  不可变对象:是一个对象中的状态(即该对象在内存中的内容)一旦被实例化就不能修改

类似于String类型(.net中 string)

  不可变的Message本质上是线程安全的.没有线程可以修改不可变Message的内容,所以接收到原始Message的第二个线程不必担心以前的线程有修改Message的可能

因此,在Akka中,所有消息都是不可变的,线程也是安全的,这也是为什么可以让上千万Akka的Actor同时处理消息的原因之一.正式不可变的Message消除了同步机制和其他不必要的代码

Actor的行为

我们大概了解了Actor和Message,那这些如何用?

Actor通过Message进行通信

在OOP中,对象通过函数调用与其他对象进行通信.A类在B类上调用一个函数,并等待该函数返回,然后A类可以继续其余工作.在Akka和Actor模型中,Actor之间通过Message进行通信

那有什么特别的呢?

  对于初学者来说,消息传递是异步的,发送Message的Actor可以在接收的Actor处理发件人的Message时继续执行其他工作.所以Actor之间的互动,默认情况下都是异步的

还有另一个变化

  由于所有"函数调用"都被Message代替,即对象的不同实例,因此Actor可以存储其函数调用的历史记录,甚至延迟处理一些函数调用

想象一下,如何使用一个Actor来构建像Microsoft Word中的“撤消”按钮这样的功能?默认情况下,你有一个Message代表每个人对文档所做的更改.要撤消其中一个更改,只需要将消息从UndoActor的垃圾邮件中删除,并将该更改推送回管理Word文档的当前状态的另一个Actor。在实践中这是一个非常强大的概念。

Actor发送Message到地址,而不是直接发送给Actor

位置透明

  什么是位置透明,位置透明意味着无论在何时向Actor发送Message,都不需要知道他们在系统中的位置,可能这个Message覆盖数百台计算机,所以只需要知道Actor的地址.

就像给别人打电话一样,你只需要知道电话号码,其他的电信供应商就会帮你连通.

Actor的工作方式也是一样,每个Actor都包含了以下部分地址

Protocol(协议):就像网络上使用的Http和Https一样,Akka支持多种传输协议用于进程间的通信.单进程Actor系统默认协议是akka://如果使用的远程或者集群,则通常会使用akka.tcp://或者akka.udp://在节点之间进行通信

ActorSystem(Actor系统):在akka的每个ActorSystem实例必须在启动时赋予一个名称,该名称可以由所有参与分布式的多个进程或者计算机共享ActorSystem.

Address(地址):如果不使用远程处理,则ActorPath可以省略这一部分,这部分是用来传达用于Actor系统之间的远程通信的具体IP地址/域名和端口信息

Path(路径):这是一个特定Actor在地址上的路径,结构就像一个网站的Url,所有用户定义的actor都是在/user/下

因此,要向Actor发送消息:

.Net
  1. //local actor
  2. var actorRef = MyActorSystem.Selection("/user/myActor");
  3. actorRef.Tell("HI!");
  4.  
  5. //remote actor
  6. var remoteActorRef = MyActorSystem.Selection("akka.tcp://MyActorSystem@localhost:1001/user/myActor");
  7. remoteActorRef.Tell("HI!");
Scala
  1. //local actor//local actor
  2. val actorRef = system.actorSelection ("/user/myActor")
  3. actorRef ! ("Hi!")
  4.  
  5. //remote actor
  6. val remoteActorRef =system.actorSelection ("akka.tcp://MyActorSystem@localhost:1001/user/myActor")
  7. remoteActorRef ! ("Hi!")
Java
  1. //local actor//local actor
  2. ActorSelection actorRef = system.actorSelection ("/user/myActor");
  3. actorRef .tell("Hi!",getSelf());
  4.  
  5. //remote actor
  6. ActorSelection remoteActorRef =system.actorSelection ("akka.tcp://MyActorSystem@localhost:1001/user/myActor");
  7. remoteActorRef.tell("Hi!",getSelf());

向远程Actor发送Message就像本地Actor一样,这就是位置透明的意思

发送给Actor地址的所有Message都是放置在属于Actor的"邮箱"中

当向Actor发送Message时,该Message不会直接进入Actor的OnReceive方法.

Message被放置在按照FIFO(先进先出)顺序排列的"邮箱"中,就像C#中Queue<T>(java中LinkedList)数据结构一样.邮箱有一个非常简单的工作,接收和挂起邮件,直到Actor准备好处理它们.

当Actor准备处理Message时,邮箱将把Message推送到Actor的OnReceive方法中,并运行Actor的消息处理方法.

Actor只能一次处理一个Message

Akka中保证参与者处理Message时,Actor的上下文和内部状态都是线程安全的

之所以这样的原因是:

  因为Message是不可变的,所以每个Message的内容本质上是线程安全的

  因为Message是串行处理的,所以更改一个actor的内部状态和上下文都不需要跨多个线程进行同步

因此,一个Actor在它的OnReceive方法退出之前,都无法处理下一个Message.当处理完时,邮箱会将下一个可用的Message推送到OnReceive方法中

Actor可以拥有内部状态

就像任何类一样,Actor可以拥有自己的属性和字段

当一个Actor重启时,actor实例就像我们这个BasicActor类的一个实例一样被销毁并重新创建.

BasicActor创建新的一个实例,通过Props把构造函数的参数传递给新的实例

Actor有一个明确的生命周期

在Actor可以从邮箱中开始处理Message之前,必须由Actor系统进行实例化并运行其生命周期.

Actor被创建并启动,然后会花费大部分时间接收消息,如果不再需要Actor,可以终止或者停止Actor

  如果Actor以外崩溃(即抛出未处理的Exception),Actor的父级将从头开始自动重启Actor的生命周期,而不会丢失仍在Actor邮箱中的剩余的Message

结合之前的例子BasicActor中实现这个生命周期:

  Actor's constructor(构造函数):BasicActor没有中并没有声明,而是使用了默认的构造函数,当然也可以使用任何带参数的构造函数

  PreStart:这是在actor可以开始接收消息之运行,是放置初始化逻辑的好地方,在重启时会被调用

  PreRestart:如果Actor意外失败(即抛出未处理的Exception),Actor的父级会重启Actor

  PostStop:一旦Actor停止并且不再接收消息,就会被调用,这里可以处理清理引用对象,PostStop在Actor重启时不会调用,只有在人为关闭时才调用

  PostRestart:在PreRestart之后,PreStart之前被调用,这是可以处理崩溃错误和诊断报告

每个Actor都有父级,有的有子级

 就像人一样,Actor有父母,有的有祖父母,兄弟姐妹和孩子

 

这意味着每个Actor都必须由其他Actor创建,所以我们代码:

.Net
  1. var actorRef = MyActorSystem.ActorOf(Props.Create<BasicActor>(), "myActor");
  2. actorRef.Tell("HI!");
Scala
  1. val system = ActorSystem("MySystem")
  2. val actorRef = system.actorOf(Props.create(classOf[BasicActor], "myActor"))
  3. actorRef ! ("Hi!")
Java
  1. final ActorSystem system = ActorSystem.create("MySystem");
  2. ActorRef actorRef= system.actorOf(Props.create(BasicActor.class,"myActor"));
  3. actorRef.tell("Hi!",getSelf());

在/user/根Actor下创建一个新的Actor,新的Actor的路径就是/user/myActor

同样,还可以在BasicActor中创建其他Actor

.Net
  1. protected override void OnReceive(object message)
  2. {
  3. var childActor = Context.ActorOf(Props.Create<BasicChildActor>(), "child1");
  4. childActor.Tell("Hi!");
  5. }
Scala
  1. override def onReceive(message: Any): Unit = {
  2. val childActor = context.actorOf(Props.create(classOf[BasicChildActor]), "child1")
  3. childActor ! ("Hi!")
  4. }
Java
  1. @Override
  2. public void onReceive(Object message) throws Throwable {
  3. ActorRef childActor = getContext().actorOf(Props.create(BasicChildActor.class), "child1");
  4. childActor.tell("Hi!",getSelf());
  5. }

这样childActor的路径就是/user/myActor/child1/

父级监督子级

在关于Actor生命周期的部分中,提到了"Actor是由他们的父级进行重启"的概念.这就类似于,每个家长都收到他们孩子发的特别的消息"求助,我要崩溃了".

每个父级都带有默认的SuperviserStrategy对象(可以自定义).该对象决定了如何处理他们的子级Actor的失败.有三种方式:

  Restart:重启失败的Actor,父级默认方式,除非子级在60秒内反复重启

  Stop:永久停止失败的Actor

  Escalate:将决定交给父级的父级处理

当发出一个Restart或者Stop 的Message,受影响的所有的子级包括自己都会重启或者停止.当然,也可以重启失败的Actor的actor家族树整个部分

引荐:https://petabridge.com/blog/akkadotnet-what-is-an-actor/

什么是Actor的更多相关文章

  1. [翻译]AKKA笔记 -ACTOR SUPERVISION - 8

    失败更像是分布式系统的一个特性.因此Akka用一个容忍失败的模型,在你的业务逻辑与失败处理逻辑(supervision逻辑)中间你能有一个清晰的边界.只需要一点点工作,这很赞.这就是我们要讨论的主题. ...

  2. [翻译]AKKA笔记 - ACTOR生命周期 - 基本 -5

    原文地址:http://rerun.me/2014/10/21/akka-notes-actor-lifecycle-basic/ (请注意这了讨论的生命周期并不包括 preRestart 或者pos ...

  3. actor concurrency

    The hardware we rely on is changing rapidly as ever-faster chips are replaced by ever-increasing num ...

  4. .NET的Actor模型:Orleans

    Orleans是微软推出的类似Scala Akka的Actor模型,Orleans是一个建立在.NET之上的,设计的目标是为了方便程序员开发需要大规模扩展的云服务, 可用于实现DDD+EventSou ...

  5. [Scala] akka actor编程(一)

    Akka基础 Akka笔记之Actor简介  Akka中的Actor遵循Actor模型.你可以把Actor当作是人.这些人不会亲自去和别人交谈.他们只通过邮件来交流.  1. 消息传递 2. 并发 3 ...

  6. 如何在插件中添加Actor类

    只要按照BlueprintFunctionLibrary的那个模板做就好了,把用编辑器生成的代码复制到插件对应的目录 之后需要注意做以下修改 1.H文件中的 GENERATED_UCLASS_BODY ...

  7. scala中的Actor

    1.介绍 2.简单示例 3.第二个程序 4.通信程序 package day01 import scala.actors.Actor case class Message(content: Strin ...

  8. UE4 中在 Actor 中动态 Create Component 与ChildActor 的 小笔记

    Note:旧版本的UE4 的Attach 和12.13版本有些不一样 创建Component: UCpp_MyComponent* temp_imageCom = NewObject<UCpp_ ...

  9. akka实现的actor

    定义一个 Actor 类 要定义自己的Actor类,需要继承 Actor 并实现receive 方法. receive 方法需要定义一系列 case 语句(类型为 PartialFunction[An ...

  10. ERROR actor.OneForOneStrategy: org.apache.spark.SparkContext

    今天在用Spark把Kafka的数据往ES写的时候,代码一直报错,错误信息如下: 15/10/20 17:28:56 ERROR actor.OneForOneStrategy: org.apache ...

随机推荐

  1. Solr vs. Elasticsearch谁是开源搜索引擎王者

    当前是云计算和数据快速增长的时代,今天的应用程序正以PB级和ZB级的速度生产数据,但人们依然在不停的追求更高更快的性能需求.随着数据的堆积,如何快速有效的搜索这些数据,成为对后端服务的挑战.本文,我们 ...

  2. 1036: [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 7496  Solved: 3078[Submit] ...

  3. Visual Studio 2017十五项新功能体验

    Visual Studio 2017正式已经于2017.3.7号正式发布,选在这一天发布也是为了纪念Visual Studio 二十周年.MVP 2017技术峰会将于这个周末(3.17)在北京举办,由 ...

  4. Internet Information Services安装与启动

    Internet Information Services安装 1.打开控制面板——程序——启动或关闭windows功能 2.找到Internet Information Services ——将其全 ...

  5. Android权限解释

    属性 说明 android.permission.ACCESS_CHECKIN_PROPERTIES 允许读写访问 "properties"表在checkin数据库中,改值可以修改 ...

  6. Git修改提交注释

    修改本地最近一次已提交的注释 git commit --amend 如果已经上传到了github上,因此github的提交和已修改的提交不一样,推送到远程可以用下面命令强制修改 git push or ...

  7. Dive in python Chapter3 实例

    def buildConnectionString(params): """Build a connection string from a dictionary Ret ...

  8. win32 htmlayout点击按钮创建新窗口,以及按钮图片样式

    最近在做一个C++ win32的桌面图形程序,我不是C++程序员,做这个只是因为最近没什么java的活. windows api,之前接触的时候,还是大学,那时用这个开发打飞机游戏纯粹是娱乐.现在基本 ...

  9. Linux之例行(任务调度)

    一. 例行命令之at 1.1 at 仅执行一次就从Linux任务中取消  1.2 at 指令可以将工作命令写入工作记录文件,工作记录文件默认存放在/var/spool/at目录内  1.3 at 工作 ...

  10. MCMC(四)Gibbs采样

    MCMC(一)蒙特卡罗方法 MCMC(二)马尔科夫链 MCMC(三)MCMC采样和M-H采样 MCMC(四)Gibbs采样 在MCMC(三)MCMC采样和M-H采样中,我们讲到了M-H采样已经可以很好 ...