Akka系列---什么是Actor

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

 

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

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

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

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

 

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

以下是Akka中基本例子

using System;

using Akka.Actor;


namespace ActorsSendingMessages

{

    /// <summary>

    /// 在Akka.NET中Actor都继承于UntypedActor。/// </summary>

    public class BasicActor : UntypedActor

    {

        protected override void PreStart()

        {


        }


        protected override void PreRestart(Exception reason, object message)

        {


        }


        protected override void OnReceive(object message)

        {

            //handle messages here

        }


        protected override void PostStop()

        {


        }


        protected override void PostRestart(Exception reason)

        {


        }

    }

}

什么是Message

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

.Net
protected override void OnReceive(object message)
{    //handle messages here

}

该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

//local actor

var actorRef = MyActorSystem.Selection("/user/myActor");

actorRef.Tell("HI!");


//remote actor

var remoteActorRef = MyActorSystem.Selection("akka.tcp://MyActorSystem@localhost:1001/user/myActor");

remoteActorRef.Tell("HI!");

向远程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
var actorRef = MyActorSystem.ActorOf(Props.Create<BasicActor>(), "myActor");
actorRef.Tell("HI!");

 

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

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

.Net
protected override void OnReceive(object message)
{    var childActor = Context.ActorOf(Props.Create<BasicChildActor>(), "child1");childActor.Tell("Hi!");
}

这样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/


原文地址:http://www.cnblogs.com/yangleiblog/p/6766197.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

架构师的工作都干些什么?!想做架构师必看

转载自 架构师的工作都干些什么&#xff1f;&#xff01;想做架构师必看 之前有网友说想看架构师升级的文章&#xff0c;所以写了本文。先给本文中架构师做个定义&#xff1a;第一&#xff0c;能力上达到&#xff08;似乎是废话&#xff09;&#xff0c;第二&#xff0c;公司…

使用java将字符串写入到指定的文件中

指定的字符串&#xff1a; 白天走在街道上&#xff0c;伸手不见五指&#xff0c;周围的建筑全被雾笼罩了&#xff0c;在家里通过窗户往外望去&#xff0c;外面就像仙境一般&#xff0c;雾把所有的东西都淹没了&#xff0c;能看到的&#xff0c;只有白色的雾。&#xff01;文件…

Mybatis入门 使用XML

1、项目结构 2、详细代码 数据库&#xff1a; 1、创建实体类bean package com.itheima.domain;import java.io.Serializable; import java.util.Date;/*** Created by Administrator on 2019/10/11.*/ public class User implements Serializable {private Integer id;priv…

.NET的一点历史故事:擦肩而过的机遇

Sun 公司曾经借由 SunOS/Solaris 这个 UNIX 操作系统&#xff0c;SPARC 硬件平台和 Java 语言建立了一个商业帝国。Sun 工作站应该是很多资深业界大佬们偶尔还会拿来回忆的一个经典产品。不过时间进入二十一世纪第一个十年的中段&#xff0c;它已经在竞争对手的轮番进攻下显出了…

Java 中的 String 真的是不可变的吗

转载自 Java 中的 String 真的是不可变的吗 我们都知道 Java 中的 String 类的设计是不可变的&#xff0c;来看下 String 类的源码。 public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for char…

Mybatis入门 使用注解

使用XML方式地址为Mybatis入门 使用XML 1、目录结构 2、需要修改的地方 1、mybatis的配置文件 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.…

浅析如何在Nancy中生成API文档

前言 前后端分离&#xff0c;或许是现如今最为流行开发方式&#xff0c;包括UWP、Android和IOS这样的手机客户端都是需要调用后台的API来进行数据的交互。 但是这样对前端开发和APP开发就会面临这样一个问题&#xff1a;如何知道每个API做什么&#xff1f; 可能&#xff0c;…

一文告诉你如何导出 Git 变更文件

转载自 一文告诉你如何导出 Git 变更文件 有时候我们想导出某次版本提交时有哪些变更的文件&#xff0c;在 svn 中有一个 export 功能&#xff0c;很方便&#xff0c;如下图所示。 在 Git 中我也找到了以下两种方法。 方法1 使用 git 自带命令 git archive, 语法如下。 g…

Entity Framework Core的贴心:优雅处理带默认值的数据库字段

对于用于保存记录添加时间的数据库日期字段&#xff0c;我们通常会设置一个 GETDATE() 的默认值&#xff0c;而不是在应用程序的代码中获取当前时间进行保存&#xff0c;这样可以避免由于web服务器时钟不同步引起的时间偏差。 Entity Framework Core 在设计时贴心地考虑到这个…

JS中使用工厂模式创建对象

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <script>function createPerson(name , age ,gender){//创建一个新的对象var obj new Object…

深度历险:Redis 内存模型详解

转载自 深度历险&#xff1a;Redis 内存模型详解 Redis 是目前最火爆的内存数据库之一&#xff0c;通过在内存中读写数据&#xff0c;大大提高了读写速度&#xff0c;可以说 Redis 是实现网站高并发不可或缺的一部分。 我们使用 Redis 时&#xff0c;会接触 Redis 的 5 种对…

.NET的一点历史故事:误入歧途,越陷越深

移动计算的时代其实早已有了苗头&#xff0c;起码微软的 Windows CE、诺基亚的 Symbian 都曾经给手机用户多少带来了一些便利&#xff0c;黑莓则由于接入了企业邮件等商业应用而日进斗金&#xff0c;甚至 Sun 也给自己的 Java 平台做了一个 J2ME 标准&#xff0c;试图用 J2EE 那…

bootstrap样式代码案例

运行结果如下所示&#xff1a; 代码如下所示&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width,user-scalableno,initial-scale1.0,maximum-scale1.0,mi…

JS中用构造函数创建对象

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <script>/* 构造函数的执行流程&#xff1a;* 1.立刻创建一个新的对象* 2.将新建的对象设置…

SELECT * FROM user WHERE username LIKE #{aaa}与SELECT * FROM user WHERE username LIKE '%${value}%'

在Mybatis中模糊查询like有两种写法&#xff1a; 第一种为SELECT * FROM user WHERE username LIKE #{aaa} 另一种SELECT * FROM user WHERE username LIKE ‘%${value}%’ LIKE #{aaa}执行的SQL为&#xff1a; 使用的是&#xff1f;占位符&#xff1a;对用的是preparedStatem…

bootstrap组件的案例代码

运行结果如图所示&#xff1a; 代码如下所示&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width,user-scalableno,initial-scale1.0,maximum-scale1.0,mi…

了解 C# foreach 内部语句和使用 yield 实现的自定义迭代器

在本期专栏中&#xff0c;我将介绍我们在编程时经常用到的 C# 核心构造&#xff08;即 foreach 语句&#xff09;的内部工作原理。了解 foreach 内部行为后&#xff0c;便可以探索如何使用 yield 语句实现 foreach 集合接口&#xff0c;我将对此进行介绍。 虽然 foreach 语句编…

JS中的原型

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><script type"text/javascript">/** 原型 prototype* * 我们所创建的每一个函数&#xff0c;解析器都会向函数中添加一个属性prototype* …

扫盲,为什么分布式一定要有Redis?

转载自 扫盲&#xff0c;为什么分布式一定要有Redis? 考虑到绝大部分写业务的程序员&#xff0c;在实际开发中使用 Redis 的时候&#xff0c;只会 Set Value 和 Get Value 两个操作&#xff0c;对 Redis 整体缺乏一个认知。所以我斗胆以 Redis 为题材&#xff0c;对 Redis …

Mybatis insert操作细节【ID】

默认情况下映射文件中插入数据&#xff1a; <insert id"saveUser" parameterType"com.itheima.domain.User">INSERT INTO user (username,address,sex,birthday) VALUES (#{username},#{address},#{sex},#{birthday})</insert>单元测试 Testp…