akka学习之actor引用,路径和地址

      这章主要介绍在分布式akka系统中actor是如何识别和寻址的。它的主要思想是actor系统本身形成的内在监护层级结构以及跨多个网络节点中actor之间的通信是位置透明的。

akka学习之actor引用,路径和地址_第1张图片
 上面这张图描述了akka系统中主要的几种实体之间的关系,下面将对这张关系图做详细介绍。

 

actor引用

       actor引用是actorRef的子类,它最重要的作用就是支持发生消息给它所代表的actor对象。actor对象可以通过self属性访问它自己的actor引用。如果使用actor发送消息,那么发送的消息中也默认包含了这个actor引用;如果actor是处理消息,那么这个actor可以通过sender属性来获取发送actor的actor引用。

       根据配置的不同,actor系统可以支持下面几种不同类型的actor引用:

1.如果actor系统的配置不支持网络功能,那么只能支持纯本地actor引用,通过网络连接到远程jvm将不起作用。

2.如果actor系统的配置支持网络功能,那么本地引用将代表该jvm中的actor对象实现网络功能,为了使得这些引用发送到远程节点之后,还能被远程节点访问,这些引用包含了协议信息和地址信息。

3.还有一种本地actor引用的子类,它包含了路由信息,被用来实现路由功能。它的逻辑结构与上面说的本地actor引用是一样的,不同的是通过路由引用发送消息之后,路由引用会直接分配消息给它代表的那些子actor。

4.如果需要通过网络访问远程actor,那么通过远程actor引用能够访问到actor。通过远程actor引用发送消息,它会先将消息序列化,然后将消息发送给远程jvm。

5.为特殊场景设计的几种特殊actor引用,这些actor引用与本地actor引用的行为是类似的。

  a.Promise actor的引用PromiseActorRef,表示一个actor完成响应完成之后的promise,akka.pattern.ask创建这个actor引用。

DeadLetterActorRef,但是它仍保证了actor的路径,以便系统能把它发送到网上,并且可以把它与系统上存在的其他actor比较路径,因为有些系统可能在它死之前被其他actor持有了。

6.某些你看不见的内部实现的一次性actor引用。

   a.仅仅作为跟守护者actor的伪监护者,它不代表一个真正的actor。

   b.在真正启动actor创建模块之前就启动了的日志服务系统,其实也是个伪actor引用。它接收日志事件,并且直接打印到标准输出上,即Logging.StandardOutLogger。

 

 actor路径

 因为actor的创建是在严格的层次体系系统中,沿着子actor到父actor的监管链一直到actor系统的根存在一条唯一的actor序列串,也就是actor的名字。这个序列串可以被视为文件系统中的封闭文件夹,所以用路径这个词来表示它,在一些真正的文件系统中,也叫做符号链接,比如一个actor对象能够通过多条路径被访问,除了原始路径外,其它的路径都包含到actor实际的监管祖先链的转换方法。这些特性将在下面的内容中介绍。

      一个actor路径包含一个actor系统的锚,锚后面跟着具体的从根守护者到目标actor的路径元素。这些路径元素是经过的actor的名字,他们之间用斜杠隔开。

 

 actor引用与actor路径的区别

      一个actor引用表示单个的actor对象,并且actor引用的生命周期与actor对象的生命周期是一样的。而actor路径只是代表了一个actor的位置,这个位置上有可能有actor对象,也可能没有,并且路径本身是没有生周期的,是不会失效的。你可以创建一个actor路径而不创建这个actor对象,但是你不能不创建actor对象而只创建actor引用。注意:这个定义不适用于actorFor方法,这也是为什么要用actorSelection替代actorFor的原因。

    你可以创建一个actor并终止它,然后在同样的路径下创建洗个新的actor,新创建的actor对象是actor类的新对象,与老的actor对象不是同一个对象。老的actor对象的引用不是新对象的引用。发送给老的actor引用的消息不会发送给新actor对象,即使他们拥有相同的路径。

 

actor路径锚

每一个actor路径都有一个地址组件,用来描述相应的actor对象的协议和位置,地址组件后面跟着从根守护开始的层级actor名字。下面是一个例子:

1、 纯本地地址:"akka://my-sys/user/ervice-a/worker1"

2、远程访问路径:"akka.tcp://[email protected]:8080/user/service-b"

在第二个中,akka.tcp是akka 2.2版中默认的传输协议。其他的传输协议是可插拔的,比如使用UDP访问远程主机可以使用akka.udp。主机和端口号(host.example.com:8080)的解析依赖于具体的传输机制,但是必须遵循URI结构规则。

 

逻辑actor路径

       从根守护者一直链接到某个actor的父监护者的唯一路径,称为逻辑actor路径。这条路径精确配到actor的所有祖先创建。因此,一旦actor系统配置和地址组件设置好之后,那么每个actor的路径也就确定好了。

 

物理actor路径

      逻辑actor路径描述的是一个actor系统的功能路径,基于配置的远程依赖意味着一个actor可以与它的父actor处于不同的actor系统和网络节点上。在这种场景下,从根守护者沿着actor路径追溯网络地址,是比较费力的操作。因此,每一个actor还有一个物理路径,物理路径的起点就是这个actor所在的actor系统的根守护。

当请求其他actor时,使用物理路径作为发送引用会使得目标actor的直接作出响应,并最小化了路由的延迟。

      一个需要特别注意的是物理逻辑绝不会跨越多个actor系统和jvm,这意味着一个被远程监护的actor对象的逻辑逻辑(监护层次)和物理路径(actor依赖)可能会不一致。

 

如何获得actor引用

     获得actor引用大致有两种:创建actor对象和查找。查找的实现又可以分为2种:通过具体的actor路径创建actor对象和请求逻辑actor层次结构。

 

创建actor对象

      一般一个actor系统最开始的行为是用ActorSystem.actorOf或者ActorContext.actorOf(对于已存在的actor系统来扩展actor树)方法在根守护的下面创建actor对象。这些方法返回新创建actor对象的引用。actor的父actor,它自己已经他的子actor可以直接访问它的引用(通过ActorContext)。这些引用可以包含在消息中被发送出去,使得他们能直接响应。

根据具体路径查找

       也可以使用ActorSystem.actorSelection方法来查找actor引用。这种方式主要用在actor通信中一个actor查询另外一个actor的引用来发送消息。

       为了获得与某个特殊actor对象生命周期相关联的ActorRef ,你需要发送消息诸如内建的身份消息给这个actor,这个actor回复的sender()的返回值就是它的引用。

      注意:推荐使用actorSelection 而不使用actorFor的原因是,对于本地actor和远程actor,actorFor的行为不一致。在被动actor引用中,这个actor在查找前必须存在,否则将返回一个EmptyLocalActorRef引用,即使在返回值之后这个actor被创建了。而在远程场景中,给通过actorFor获得的actor引用发送消息,没次发送消息都会悄悄地通过远程actor系统的路径查找这个actor。

 

绝对路劲VS相对路径

除了ActorSystem.actorSelection方法,还有ActorContext.actorSelection方法。两种不同的是,前者的路径必须是从根守护开始到目标actor。而后者可以从当前actor开始的路径,路径元素用“..”来访问父actor。你可以通过下面两种方式来给兄弟姐妹发送消息:

1.相对路径:context.actorSelection("../brother")!msg

2.绝对路径:context.actorSelection("/user/serviceA")!msg

 

请求逻辑actor层次

 actor系统的层级结构像文件系统一样,因此像使用支持unix shells的方式一样去匹配路径是可能的:你可以使用通配符替代路径元素来匹配多个实际actor。因为这种结果可能不是单个actor引用,所以返回值是不同类型的ActorSelection,也不支持像操作单个actorRef一样来操作一个集合。使用ActorSystem.actorSelection和ActorContext.actorSelection方法并用通配符匹配出来的actor引用,可以是下面方式发生消息:context.actorSelection("../*") ! msg。这行代码将向所有的兄弟姐妹,包括自己发生消息。而通过actorFor方法获得actor引用,为了发送所有而定消息,需要访问整个监护层级。通过模糊匹配找出来的actor集合可能发生改变,即使消息正在给接收者的路上,而监控所有匹配上的actor的变化情况是不可能的。为了解决发生请求和接收响应的不确定性,先要把发送者引用提取出来,然后监控所有的接收actor。这个问题可能在后面的akka版本中解决掉。

 

总结actorOf  vs   actorSelection vs actorFor

1.actorOf仅用来创建一个新的actor对象,新创建的actor对象是actorOf所在的context(可能是任何一个actor或者actor系统)的直接子actor。

2.actorSelection仅仅是用来在发送消息时查找已经存在的actor,他不创建actor,也不会再actor创建之后去检查它的存在性。

3.actorFor(被actorSelection)取代,仅仅是查找已存在的actor。

 

 actor引用和actor路径的相等性

       actor引用的相等性是指指向同一个actor对象的引用。如果两个actor引用有相同的路径,并且指向同一个actor对象,那么认为这两个actor引用是相等的。指向一个终止的actor对象不等于另一个指向拥有相同路径的actor对象(重新创建的)。但是由于错误重启的actor意味着它还是原来的actor对象,因此actor的重启对了持有它的actor来说是透明的。

      通过actorFor获取的远程actor引用没有包含识别一个actor身份的完整信息,所有这类引用不等于通过actorOf,sender或context.self获取的引用。

      如果你想再集合中跟踪actor引用而不关心actor对象本身,那么可以使用ActorPath作为key。因为在比较actor路径的时候,actor对象本身是不考虑在内的。

 

复用actor路径

      当一个actor对象终止了,它的引用将指向死信邮箱,死亡监控将发布它的变化,并且一般来说也不会希望它死而复生(因为actor的生命周期不允许它重生)。但是随后可能会在相同路径下创建一个新的actor,不过这并不是一个很好的实践:通过actorFor获取的actor引用突然在死亡之后又重新开始工作了,并且这个转变与任何其他的事件没有任何的顺序保证,因此原来的actor拒绝的消息又开始重新被新的actor接收了。

      复用actor路径在非常特殊的环境中这样做可能是对的,但是必须确保只有于这个actor的监护者才有权做这种处理。因为只有它的监护者才能可靠的保证在新的actor创建之前,旧的actor名字从系统中注销了。

      当测试项依赖于特殊路径的实例时,也可能需要复用actor路径。在这种场景下,最好mock它的监护者,以便在测试过程中随时发送终止消息,并能在后面等待合适的名字注销。

 

与远程部署的互操作

      当一个actor创建了一个子actor,actor系统的部署者将决定新的actor对象是在同一个jvm中还是在另一个节点上。如果是在其他节点上,actor对象是由网络链接触发两一个jvm或actor系统来创建的。远程系统将会把该actor对象放置在专门为远程创建actor而保留的特殊路径上,它的监护者是触发创建这个actor的那个远程actor。在这种场景下,context.parent(监护者引用)和context.path.parent(在actor路径中的父节点)代表不同的actor对象。但是,通过监护者寻找子actor名字时将会在远程节点找到它,而且这个名字仍然保持着它的逻辑结构名,比如向另一个未确定的actor发送消息时。


akka学习之actor引用,路径和地址_第2张图片 

地址端口的用途

       当通过网络来发送一个actor引用时,用actor的路径来表示actor引用。因此路径中的地址字符串必须包含协议信息,host地址,端口号等所有必要的信息才能正确发送消息给潜在的actor。当actor系统接收到一个来自远程节点的actor路径时,它首先要检查这个路径的地址是否与自己匹配,地址匹配通过后才将这条路径转换为本地引用,否则认为它是一个远程actor引用。

 

actor路径的顶层作用域

     在actor路径的层级结构的根部,存放的是根守护actor,通过它可以找到所有的actor。根路径的名字是一个斜杠“/”,后面跟着下面几种层级。

1.“/user”。所有用户创建的第一级actor的根守护actor。通过ActorSystem.actorOf创建的actor都能在“/user”下面。

2.“/system”。所有系统创建的第一级actor的根守护actor。比如日志监听actor和系统启动时读取配置创建的actor。

3.“/deadLetters”。所有发送给已终止或不存在的actor的信息都会重新路由到死信actor(即使在同一个jvm中,信息还是可能丢失)。“/deadLetters”是死信actor的根守护actor。

4.“/temp”。系统创建的生命周期短的actor的根守护。比如ActorRef.ask的实现中有用到。

5."/remote"。这是一个虚假的路径,所有监护者是远程actor引用的actor都在这个路径下存放。

    上面这套构造actor的命名空间的需求源于一个非常简单集中的目的:系统层级中的每个actor都是一个actor,并且所有的actor都以同样的方式工作。因此,你不仅仅只需要查找你自己创建的actor,你还可能查找系统守护者并向它发送消息(虽然它会把你的消息丢掉)。什么是最有力的原则?最有力的原则就是它使整个系统统一和一致,没有乱七八糟的例外情况需要记忆。

 

      

 

 

 

 

 

 

 

 

你可能感兴趣的:(akka)