首页 > 代码库 > AKKA文档(java版)

AKKA文档(java版)

AKKA文档(java版)

目前我正在翻译AKKA官网文档。翻译:吴京润

译者注:本人正在翻译AKKA官网文档,本篇是文档第一章,欢迎有兴趣的同学加入一起翻译。更多内容请读这里:https://tower.im/projects/ac49db18a6a24ae4b340a5fa22d930dc/lists/ded96c34f7ce4a6bb8b5473f596e1008/show/  https://tower.im/projects/ac49db18a6a24ae4b340a5fa22d930dc/todos/640e53d6e8c149ab95c47cd333b91073/

这是文档目录

1.    介绍

1.1    什么是AKKA?

1.2    为什么选择AKKA?

1.3    AKKA入门

1.4    hello world

1.5    用例与部署场景

       1.6    akka的用例示例

2.   综述

       2.1   术语,概念

       2.2   角色系统

       2.3   什么是角色

       2.4   监管和监测

       2.5   角色的引用、路径和地址

       2.6 位置透明性

       2.7 Akka与Java内存模型的关系

       2.8 消息传递的可靠性

       2.9 配置

3.  角色

       3.1 角色

       3.2 类型化的角色

 

 

AKKA文档(java版)——什么是AKKA?

原文:http://doc.akka.io/docs/akka/2.3.6/intro/what-is-akka.htmll  译者:吴京润

可扩展的实时事务处理

我们相信编写并发、容错、可扩展的应用相当的困难。盖因大多数时候我们一直在使用错误的工具和错误的抽象等级。AKKA就是为了改变这一切的。我们利用角色模型提升了抽象等级,并且提供了一个用来构建可扩展的、弹性的以及响应式应用的更好的平台——更多信息请见Reactive Manifesto。对于容错机制我们采用“让它崩溃”模型,这一模型已在电信行业取得了巨大的成功,旨在构建自我修复与永不停机的系统。角色还提供了透明的分布式抽象以及真正的可扩展且容错应用的基础。

AKKA是开源的,并遵守Apache2许可。

从http://akka.io/downloads下载。

请注意所有代码样例的编译,因此如果你想直接访问这些源码,可以去github上访问AKKA文档子项目:Java和Scala。

AKKA实现了一个独特的混合

角色

角色给你提供了:

  • 并发与并行性的高等级抽象
  • 异步、无锁以及高性能的事件驱动编程模型
  • 非常轻量级的事件驱动流程(1GB堆内存可容纳几百万的角色)

参考相关章节:Scala或Java

容错

  • 拥有“让它崩溃”语义的管理层级
  • 管理层级可跨越多个JVM,实现真正的容错系统
  • 非常适合编写可自我修复且永不停机的高容错能力的系统

参考相关章节:Scala和Java

位置透明

AKKA的一切旨在分布式环境中工作:角色之间都使用纯消息交互,而且一切都是异步的。

对集群支持的概述请见:Java和Scala。

持久化

启动或重启角色时,可以选择持久化或重放它接收到的消息。在JVM崩溃或角色迁移到另一节点时,这一点可以使角色恢复它的状态。

更多细节请见:Java和Scala

Scala和Java API

AKKA拥有Scala和Java两种语言的接口文档。

AKKA有两种使用方式

  • 作为一个库:将它放到WEB-INF/lib下面供一个web应用使用,或把它当作一个常规的JAR放到你的类路径里。
  • 微内核方式:将你的应用放到独立的内核里。

更多信息参考用例与部署场景。

商业支持

AKKA由Typesafe Inc.按照包含开发与生产支持的商业许可证开发,更多内容请读这里。

AKKA文档(java版)—什么是角色

原文: http://doc.akka.io/docs/akka/2.3.5/general/actors.html 译者:Vitas
2.3 什么是角色?
前面角色系统一节介绍了一群角色如何形成一个层次结构,并且介绍了角色是构建应用程序的最小单位。本节我们将角色拿出来单独介绍,解释一些你在使用它的过程中可能遇到的概念。对于一些更深入的细节,将会在后面的章节中详细介绍。
你可以将角色想象成一个容器,它其中包括状态,行为,一个信箱,子角色以及一个监管策略。所有这些都封装在一个角色引用中。本节的最后介绍一个角色什么时候终结。


2.3.1 角色引用
如下所述,为了更好的发挥角色模式的优势,一个角色对象需要与外部隔离。因此,在外部通过一个角色引用来表示角色对象,这些角色对象引用可以自由的传递而不受约束。这种将对象划分为内部对象和外部引用的好处是可以使下列操作透明化:重启一个角色不需要更新角色引用,角色可以在远程机器上,可以通过完全不同的应用来给角色发消息。并且这样做最重要的好处是,外部的对象永远无法得到角色内部的状态,除非角色不明智的公开了它的信息。
2.3.2 状态
角色对象通常都包含一些变量来表明它当下处于哪个状态。这些变量可以是显示的状态机(例如,使用fsm-scala模型),也可以是一个计数器,一组监听器,甚至等待挂起等。正是这些状态数据使得角色有价值,因此必须保护它们不被其他的角色破坏。好消息是从概念上来讲每个Akka角色对象都有它自己的轻量级线程,而这个线程是与系统的其他部分完全隔离的。这也就意味着你可以放心大胆的写你的角色代码而不用考虑并发的同步问题。
在后台Akka会有一系列的真实线程来运行这一系列的角色,如有些情况下多个角色会共用一个线程。因而一个角色在接下来的调用中可能会被另一个不同的线程所处理。但是无论实现细节是怎么样的,Akka都能确保同时只有一个线程去改变角色的状态。
角色的内部状态对它的行为是至关重要的,如果出现状态不一致将会产生致命的错误。因而,当一个角色失败并被它的监管程序重启时,它的状态将被重新初始化。而这一点也使得系统有了自动恢复的能力。
可选择地,一个角色也可以在重启后通过重新处理那些老的消息来恢复到它重启之前的状态(参考 persisitence-scala)。

2.3.3 行为
每次消息的处理都会与角色当前的行为所匹配。行为指的是一个函数定义的在当前状态下对一条消息所要采取的行动。例如如果这个客户端是经过认证的则转发它的请求,否则拒绝。并且行为可能随着时间的不同而变化,例如,某些客户端可能在后来获得了认证,或者角色先在“不在服务状态”的模式后来又恢复服务了等等。这些变化可能是因为状态变量的变化引起的,也可能是函数在运行时被换出引起的,参考become 和unbecome操作。然而,角色对象重启后都会恢复到构造函数中初始化的行为。

2.3.4 信箱
角色的任务是处理来自其他角色(或者角色系统之外)发来的消息。连通消息发送者和接受着的对象就是信箱:每个角色都有一个信箱,发送者将消息发送到该信箱。消息以发送者发送消息的时间顺序入队,这也就意味着由于发送者在线程上分布的随机性,来自不同角色的消息在运行时没有一个确定的入队顺序。但是同一个发送者发送的消息顺序是一定的。
这里有很多不同的信箱实现方式可供选择,默认的方式是FIFO:消息被处理的顺序和消息入队的顺序相同。通常我们都选择默认的实现方式,但是有些应用程序可能需要对一些消息优先处理。在这种情况下,消息入队的时候就不一定是插在队尾了,而是根据它的优先级插入到对应的位置,优先级特别高甚至可能直接插在队头。当使用这种队列时,消息被处理的顺序决定于该队列的实现算法,并且通常不是FIFO.
Akka不同于其他角色模型实现的一个重要特征是当前的行为必须挨个处理队列里的下一个消息,它并不会去扫描信箱以得到一个匹配的消息。无法处理消息就会被认为是失败,除非当前的行为是忽略此消息。

2.3.5 子角色
每个角色都是一个潜在的监督员:一旦它创建了子角色并且赋予了它一个子任务,那么它将自动监管这些子角色。角色在它的上下文环境(context)中维持着一个子角色列表,以便随时访问它们。角色通过创建(context.actorOF(…))或停止(context.stop(child))命令来修改这个列表。并且这个修改立马就会生效。这个创建和终止命令是以异步的方式实现的,所以它不会阻塞监督者。

2.3.6 监督者策略
角色最后一个功能是处理子角色的故障。故障处理对于用户来说是透明的,对于发生的故障Akka都会自动的通过对应策略处理(监管和监测章节介绍)。 因为这些策略是构成角色系统的基础,所以一旦角色被创建那么这些故障处理策略将不可改变。
由于每个角色都只有一类故障处理策略,这也就意味着不同的子角色将会应用不同的策略。角色根据子角色所使用的故障处理策略将它们分组。根据任务被切分的子任务性质系统会不断的重复这个分组过程。

2.3.7 什么时候角色终止
一旦角色终止,如失败后无法重启,自己停止或者被监督者停止,那么它将释放它占有的所有资源,并将它信箱中没处理的消息发送到“死信件信箱”里,然后“死信件信箱”将会把他们以DeadLetters的形式传递给EventStream. Actor引用中的信箱将会被一个系统信箱所取代,然后将所有消息以DeadLetters的形式转发给EventStream。虽然系统会尽最大努力来实现该消息传递,但是我们不能依赖它来实现“有保证的传递”。
为什么不选择悄悄的将所有消息倾倒出来,是基于我们下面的测试结果考虑:我们在事件总线(BUS)上注册了测试事件监听器(TestEventListener)。这里事件总线即死信件被发送到的地方。该监听器将会对收到的每条死信件记录一条警告日志——这个可以帮助我们更快的检测失败。所以我们有理由相信这个特征还将可以应用于其他目的。

AKKA文档(java版)——什么是AKKA?

原文:http://doc.akka.io/docs/akka/2.3.6/intro/what-is-akka.htmll  译者:吴京润

可扩展的实时事务处理

我们相信编写并发、容错、可扩展的应用相当的困难。盖因大多数时候我们一直在使用错误的工具和错误的抽象等级。AKKA就是为了改变这一切的。我们利用角色模型提升了抽象等级,并且提供了一个用来构建可扩展的、弹性的以及响应式应用的更好的平台——更多信息请见Reactive Manifesto。对于容错机制我们采用“让它崩溃”模型,这一模型已在电信行业取得了巨大的成功,旨在构建自我修复与永不停机的系统。角色还提供了透明的分布式抽象以及真正的可扩展且容错应用的基础。

AKKA是开源的,并遵守Apache2许可。

从http://akka.io/downloads下载。

请注意所有代码样例的编译,因此如果你想直接访问这些源码,可以去github上访问AKKA文档子项目:Java和Scala。

AKKA实现了一个独特的混合

角色

角色给你提供了:

  • 并发与并行性的高等级抽象
  • 异步、无锁以及高性能的事件驱动编程模型
  • 非常轻量级的事件驱动流程(1GB堆内存可容纳几百万的角色)

参考相关章节:Scala或Java

容错

  • 拥有“让它崩溃”语义的管理层级
  • 管理层级可跨越多个JVM,实现真正的容错系统
  • 非常适合编写可自我修复且永不停机的高容错能力的系统

参考相关章节:Scala和Java

位置透明

AKKA的一切旨在分布式环境中工作:角色之间都使用纯消息交互,而且一切都是异步的。

对集群支持的概述请见:Java和Scala。

持久化

启动或重启角色时,可以选择持久化或重放它接收到的消息。在JVM崩溃或角色迁移到另一节点时,这一点可以使角色恢复它的状态。

更多细节请见:Java和Scala

Scala和Java API

AKKA拥有Scala和Java两种语言的接口文档。

AKKA有两种使用方式

  • 作为一个库:将它放到WEB-INF/lib下面供一个web应用使用,或把它当作一个常规的JAR放到你的类路径里。
  • 微内核方式:将你的应用放到独立的内核里。

更多信息参考用例与部署场景。

商业支持

AKKA由Typesafe Inc.按照包含开发与生产支持的商业许可证开发,更多内容请读这里。

AKKA文档(java版)——准备开始

原文:http://doc.akka.io/docs/akka/2.3.6/intro/getting-started.html  译者:吴京润

预备知识

AKKA要求你的计算机已经安装了Java1.6或更高版本。

入门指南与模板项目

学习AKKA的最好方式是下载Typesafe Activator并尝试一个AKKA模板项目。

下载

有许多种下载AKKA的方式。你可以把它当作Typesafe平台的一部分下载(就像上面描述的)。还可以下载完全发布版,它包含微内核以及所有模块。或者使用像Maven或SBT这样的构建工具从AKKA Maven仓库下载依赖。

模块

AKKA是调度模块化的,它由许多拥有不同特性的JAR组成。

  • akka-actor – 经典角色、类型角色、IO角色等。
  • akka-agent – 代理、整合了Scala的STM特性
  • akka-camel – 整合Apache的Camel
  • akka-cluster – 集群成员管理、弹性路由
  • akka-kernel – AKKA微内核,运行着一个极简应用服务器
  • akka-osgi – 在OSG容器里使用AKKA的基本bundle,包括akka-actor的类
  • akka-osgi-aries – Aries——服务提供角色系统的蓝图
  • akka-remote – 远程角色
  • akka-slf4j – SLF4J Logger (事件总线监听器)
  • akka-testkit – 测试角色系统的工具包Toolkit for testing Actor systems
  • akka-zeromq – 整合ZeroMQ

除了这些已进入稳定内核的稳定模块之外,还有许多标记为“试验(experimental)”的模块。这并不是意味着它们没有达到预期的功能,它的主要意义是它些模块的API尚不够稳定。你可以通过邮件列表向我们反馈这些模块的使用情况,帮助加快这一进程。

  • akka-contrib – 提交者贡献的各种模块可能会也可能不被移到核心模块,见外部贡献了解更多细节。

一个实际的JAR文件名如下所示:akka-actor_2.10-2.3.6.jar(其它模块也是如此)。

在依赖关系章节说明了各AKKA模块的JAR之间的依赖关系。

使用发布版

从 http://akka.io/downloads下载你需要的发布版并解压缩。

使用快照版

每日构建的AKKA快照版发布在http://repo.akka.io/snapshots/,并同时以SNAPSHOT和时间戳标记版本。你可以选择使用一个时间戳标记的版本并决定何时升级到一个更新版本。AKKA快照仓库还通过http://repo.typesafe.com/typesafe/snapshots/代理了AKKA模块依赖的许多其它仓库的代理。

警告

除非你知道你在做什么,否则不鼓励使用AKKA的快照版、每日构建版和里程碑版。

微内核

AKKA的发布版拥有一个微内核。将你的应用JAR放到deploy目录并运行bin 目录下的脚本。

更多信息请见微内核(Scala)|(Java)

使用构建工具

AKKA可以使用支持Maven仓库的构建工具

Maven仓库

从AKKA2.1-M2版开始:

Maven中央仓库

之前的版本:

AKKA仓库 Typesafe仓库

通过Maven使用AKKA

开始使用AKKA和Maven的最简单的方式是检出Typesafe Activator名为Akka Main in Java的教程。

既然AKKA发布到了Maven中央仓库(从v2.1-M2开始),当然可以通过POM添加AKKA依赖。比如下面就是akka-actor模块的依赖

<dependency>
    <groupId>com.typesafe.akka</groupId>
    <artifactId>akka-actor_2.10</artifactId>
    <version>2.3.6</version>
</dependency>

注意:对于快照版,SNAPSHOT 与时间戳版本同时发布。

通过SBT使用AKKA

最简单的方法是检出Akka/SBT template工程。

SBT中的AKKA的必要部分:

SBT安装指南:https://github.com/harrah/xsbt/wiki/Setup

build.sbt文件:

</pre>
<pre>name:="My Project"
version:="1.0"
scalaVersion:="2.10.4"
resolvers+="Typesafe Repository" at "http://typesafe.com/typesafe/releases/"
libraryDependencies+="com.typesafe.akka" %% "akka-actor" % "2.3.6"

注意:在上面的libraryDependencies设置中,指定SBT版本为v0.12.x或更高版本。如果你使用较早的SBT版本,libraryDependencies应像下面这样:

libraryDependencies+="com.typesafe.akka" %% "akka-actor_2.10" % "2.3.6"

通过Gradle使用AKKA

要求至少是Gradle1.4以上,包含Scala插件。

apply plugin:‘scala‘
repositories {
    mavenCentral()
}
dependencies {
    compile ‘or.scala-lang:scala-library:2.10.4‘
}
tasks.withType(ScalaCompile) {
    scalaCompileOptions.useAnt=false
}
dependencies {
    compile group: ‘com.typesafe.akka‘, name: ‘akka-actor_2.10‘, version: ‘2.3.6‘
    compile group: ‘org.scala-lang‘, name: ‘scala-library‘, version: ‘2.10.4‘
}

在Eclipse中使用AKKA

安装SBT工程然后使用sbteclipse生成Eclipse工程。

在IntelliJ IDEA中使用AKKA

安装SBT工程然后使用 sbt-idea生成IntelliJ IDEA工程。

在NetBeans中使用AKKA

安装SBT工程然后使用 nbsbt 生成NetBeans工程。

你同样应该为IDE对scala的通用支持使用 nbsbt 。

不要使用Scala编译器优化选项 -optimize

警告

从没有使用Scala编译器的-optimize选项编译或测试过akka。有尝试过的用户报告使用该选项发生了奇怪的行为。

从源码构建

Akka使用Git托管在Github上。

  • 从http://github.com/akka/akka克隆Akka仓库

更多内容参考构建Akka

需要帮助?

如果你有问题可以从Akka邮件列表获得帮助。

你也可以获取商业支持。

感谢您成为AKKA社区的一员。

AKKA文档(java版)—角色的引用、路径和地址

原文:http://doc.akka.io/docs/akka/2.3.6/general/addressing.html  译者:小鱼    审校者:吴京润

2.5 角色的引用、路径和地址

这一章描述,角色在一个有可能是分布式的角色系统中是如何被识别和定位的。它关系到了角色系统形成的内在监管层级以及角色跨越多个网络节点之间通信的位置透明化。



上述图片显示了角色系统中几个最重要实体之间的关系,请仔细阅读。

 

2.5.1什么是一个角色引用?

一个角色引用是ActorRef的一个子类型,它的主要目的是为它所代表的角色提供发送消息的功能。每个角色可以通过self字段访问自己指定(本地)的引用,这个引用包括发送者引用,默认会发送所有消息给别的角色。相反的,在消息处理期间,这个角色可以访问发送者引用,通过sender方法来呈现当前的消息。

这里提供了几个基于角色系统配置的不同类型的角色引用:

  1. 角色系统中的纯本地引用被配置成不支持网络功能的,这些角色引用发送的消息不能通过一个网络发送到另一个远程的JVM。
  2. 角色系统中的本地引用在被启用时,它代表了那些在同一个JVM里的角色支持网络功能。为了发送消息给别的网络节点,这些引用包含了协议和远程地址信息。
  3. 这里有一个用于路由器的本地角色引用的子类型(即角色混合了路由器的特性)。它的逻辑结构和上述的本地引用一样,有一点不同的是,向它们发送的消息会被分发给它们的子角色中的一个。
  4. 远程角色引用表示了哪个角色可以通过远程通信到达,即发送消息给它们会透明的序列化消息,并把它们发送给远程的JVM。
  5. 这里有几个角色引用的特殊类型,它们代表了角色引用的所有实用目的:
  • PromiseActorRef是Promise的一个特殊代表,它被用于完成一个角色的响应。akka.pattern.ask创建这个角色引用。
  • DeadLetterActorRef是死亡信件服务的默认实现,定义那些Akka路径中终点是关闭或不存在的消息。
  • EmptyLocalActorRef是Akka查找一个不存在的本地角色路径时返回的:它等同于DeadLetterRef,不过它保留了它的路径,这样Akka可以通过网络把它发送出去,然后可以通过它的路径和别的已经存在的角色引用进行比较,这样就能得到一些已经死亡的角色。
  1. 还有一些你可能还没见过的一次性的内部实现:
  • 有一个角色引用,它并不呈现为一个角色,它只作为一个伪管理员的根部守护者(guardian,下同),我们称它为“行走在时空泡沫中”。
  • 第一个日志服务开启之前,事实上激起角色创建的工具是一个伪造的角色引用,它能接收日志事件并把它们直接打印到标准输出中,它就是Logging.
  • StandardOutLogger。

 

2.5.2什么是一个角色路径?

既然角色们在严格的层级结构中创建,也就存在有一个角色名称的惟一序列,这些名称是递归地通过双亲与子女之间的监管关系命名的,一直到角色系统的根 。这个序列可以看成是文件系统中的文件夹,因此我们采用路径(path)这样的名字来关联它。在一些真实的文件系统中,它们也叫“符号链接”,即一个角色可以通过不止一条路径来访问,其中除了一个涉及到一些从角色的实际监管祖先行列中分离出来的转换。这些特性会在下面的小节中描述。

一条角色路径包含一个锚,它可以识别角色系统,后面紧跟着的是路径元素,从根守护者到指定的角色。路径元素遍历角色的名字,用斜杠隔开。

 

2.5.2.1 角色引用和角色路径之间有什么区别?

一个角色引用指定单个角色,并且它的生命周期和角色的生命周期一致;一条角色路径表示一个名字,它可能被一个角色占据,并且它没有生命周期,它不会变成无效状态。创建一条角色路径的同时可以不用创建角色,而角色引用则不一样,它必须创建与之关联的角色。

注意:这些定义是不支持actorFor的,这就是为什么弃用actorFor而选用actorSelection的原因之一。

你可以创建一个角色,终止它,然后用相同的角色路径创建一个新的角色。新创建的角色是另一个新的角色,和终止的角色不是同一个。旧角色的角色引用对于新的角色是无效的。发送给旧角色引用的消息不会交付给新的角色,尽管它们有相同的路径。

 

2.5.2.2 角色路径锚

每一个角色路径都有一个地址组件,描述协议和位置,这样使得关联的角色是可达的,接着是从层次结构根部起的角色名称。例如:

  1. “akka://my-sys/user/service-a/worker1″          // 纯本地
  2. “akka.tcp://my-sys@host.example.com:5678/user/service-b” //远程

这里的akka.tcp是2.2版本默认的远程运输协议,可以插入别的协议。一个远程主机如果使用UDP来访问,可以通过akka.udp。主机和端口部分取决于所使用的传输机制,但它必须遵循URI的结构规范。

2.5.2.3 角色的逻辑路径

通过父代监管者的链接指向的根守护者得到的唯一路径叫做角色的逻辑路径。这条路径完全匹配由祖先创建的角色,所以一旦角色系统的远程配置(和路径的地址组件)设置之后它就被完全确定。

2.5.2.4 角色的物理路径

当角色的逻辑路径描述一个角色系统内的功能位置的时候,基于配置的远程部署意味着一个角色可能创建于与它的父角色不同的网络主机上,即在不同的角色系统中。在这种情况下,从根守护者中获得角色需要遍历网络,这一操作要付出昂贵的代价。因此,每一个角色也有一个物理路径,开始于角色实际驻留的角色系统的根守护者。用这个路径作为发送者引用,当需要查询别的角色时,会让他们直接回复给这个角色,这样可以最小化由路由带来的延迟。

重要的一点就是,一个角色的物理路径不会跨越多个角色系统或虚拟机。这意味着如果角色的祖先其中有一个是远程监管,那么它的逻辑路径和物理路径会被分离开。

 

2.5.3如何获得角色引用?

有两种方法来获得角色的引用:通过创建角色或者查找,后者通过具体的角色路径和查询角色的逻辑层次来创建两种风格的角色。

2.5.3.1创建角色

一个角色系统通常通过使用ActorSystem.actorOf方法在守护者角色下开始创建角色,然后利用这个方法在已创建的角色下生成一个角色树。这些方法会返回一个引用给新创建的角色。每一个角色可以直接访问(通过它的ActorContext)到它的父角色,自己以及子角色的引用。这些引用会被包含在消息中发送给别的角色,使它们能够直接回复。

2.5.3.2通过具体路径查找角色

另外,角色引用可以通过ActorSystem.actorSelection这个方法来查找。被选择的角色可以与上述角色(审校者注:原文为the selection can be used for communicating with said actor,而本文到此为止仅提到过几种角色类型和创建角色的方式,尚未提及具体角色,本人认为所谓said actor应是前面提到的几种类型角色)通讯,而投递消息时被选择的相关角色会被查找。

为获得一个被绑定到某个特定角色生命周期的ActorRef,你需要发送一个消息(例如内置Identify的消息)给这个角色并使用角色通过sender()回复的引用。

注意:弃用actorFor而选用actorSelection的原因是,利用它来获取角色引用对于本地和远程的角色表现是不一样的。例如一个本地角色引用,被指定的Actor需要在查询之前存在,否则获得的引用将会是一个EmptyLocalActorRef,即使一个拥有确切路径的角色在获得角色引用之后被创建。通过actorFor获得的远程角色引用的行为不同,而且将消息发送给这样一个引用,会为了每一条消息的发送在远程系统中通过路径查找角色。

绝对路径 VS 相对路径

ActorSystem.actorSelection或ActorContext.actorSelection,可在任何角色内部通过context.actorSelection得到该对象的引用。在ActorSystem中,一个角色选择就像产生了一个它的双胞胎兄弟,而不是从启动它的角色所在角色树的根查找。路径元素中包含两个点(”..”)可以用来访问父角色。你可以像下面的例子一样向它的兄弟发送一条消息:

 

context.actorSelection(“../brother”) ! msg

 

通常情况下也可以通过绝对路径在上下文中进行查找:

 

context.actorSelection(“/user/serviceA”) ! msg

 

它们都能正常的工作。

2.5.3.3 在角色逻辑层次结构中查询

既然角色系统创建一个类文件系统层次结构,通过某些方式由Unix shell支持的路径匹配方式也是有可行的:你可以用通配符(“*”和“?”)来替换路径名称来表示选项,它可以匹配0个或更多的角色。因为结果不是单个的角色引用,它有一个不同的ActorSelection类型,并且不支持ActorRef的全套操作。选择集可以通过ActorSystem.actorSelection和ActorContext.actorSelection来制定,并支持发送消息:

 

context.actorSelection(“../*”) ! msg

 

上述代码会给当前角色和所有的兄弟角色发送msg。至于通过actorFor来获得引用,为了执行消息发送的功能,需要完成监管层级结构的遍历。角色集合的具体匹配可能会改变,即使一个消息已经在发送给接收者的途中,监控一个选择集的现场变化是不可能的。为了解决发送请求与收集所有应答的不确定性,需要提取出发送者引用,然后监控发现的所有具体角色。这个问题将会在未来的版本中得到改善。

概括:actorOf VS actorSelection VS actorFor

注意:上述章节内容可以概括如下,这样有助于记忆:

  1. actorOf只能创建一个新的角色,当这个方法被调用的时候(可能是任何的角色或角色系统),它会创建一个上下文的直接子角色。
  2. actorSelection只能在投递消息的时候查找已经存在的角色,也就是说选择集被创建时,既不能创建角色也不能验证当角色的存在性。
  3. actorFor(已被actorSelection取代)只能查找一个已经存在的角色,不能创建角色。

 

2.5.4 角色引用和路径等价

ActorRef的等价性体现了如下意图,即ActorRef与目标角色的化身(审校者注:原文为incarnation,意思应该是角色的实际功能、内容、引用等意思,下同)相一致。当两个角色的引用有相同的路径并指向相同的角色化身的时候就被认为是等价的。一个引用指向一个终止的角色不等价于指向另一个有相同路径的角色(重建)。注意,由于故障而重启的角色跟之前的是同一个,即重启对于ActorRef的用户来说是不可见的。

通过actorFor获得的远程角色引用不包括底层角色的完整信息,因此这些引用不等价于通过actorOf,sender或context.self获得的引用。因此actorFor被弃用,而被actorSelection代替。

如果你需要在一个集合中跟踪角色引用,并且不关心角色的真正化身,你可以用ActorPath作为键,因为在比较角色路径的时候,目标角色的标识不是要考虑的因素。

 

2.5.5 重复使用角色路径

当一个角色终止,它的引用会指向一个死信邮箱,DeathWatch会发布它最终的转换,通常不再希望起死回生(因为角色的生命周期不允许这样)。但它后面可能会创建一个路径完全相同的角色——只是由于在没有保留已经有效创建的所有角色的情况下无法执行相反的操作——这不是好的实践:通过actorFor获得的远程角色,在死亡后,又突然又开始工作,然而在这个转换和其它任何事件之间的顺序没有任何担保,因此该路径的新角色可能会接收到发送给以前角色的信息。

这可能是在非常特殊情况下的正确的事情,如果恰恰是角色监管人的话,就要去限制处理,因为它是唯一一个可以可靠检测正确注消命名的角色,一旦它出问题,之前新创建的子角色都会失败。

当被检测者不得不在特定路径上实例化的时候,在测试期间也可能要求这样(审校者注:此处应指重复使用角色路径)在这种情况下,最好去模拟它的监管人,这样它就可以在测试过程中把终止信息投递给合适的节点,使得后者能够等待名字正确的取消。

 

2.5.6 远程部署的相互影响

当一个角色创建子角色,角色系统的发布者会决定是否把新的角色放在同一个JVM或另一个节点上。第二点,角色的创建会在不同的JVM之间触发一个网络连接,从而包含在不同的角色系统中。远程系统会把新的角色放置在一个指定的路径下,新角色的监管者会是一个远程角色引用(表示创建它的角色)。在这个情况下,context.parent(监管者引用)和context.path.parent(这个角色路径中的父节点)不是同一个角色。然而,在监管者内部查找子角色的名字,会在远程节点上找到它,本地仅保持逻辑结构,例如给未定(审校者注:原文为resolved actor)角色引用发送消息。



2.5.7 地址部分用来干什么?

当在网络中发送一个角色引用,它通过路径来表示。因此,该路径必须充分编码,把底层角色需要的所有信息发送给它。这一点通过如下方式实现:通过把编码协议、主机和端口作为路径字符串的地址部分。当角色系统从一个远程节点接收到一个角色路径,它会检查该路径地址是否匹配本角色系统,如果匹配本角色系统,它被解析为本地角色引用。否则,它就是一个远程角色引用的表示。

 

2.5.8 角色路径的顶级作用域

在路径层次结构的底部有一个根部守护者,在它上面可以找到所有其它的角色;它用”/”来表示。下一个等级由如下列表所示组成:

  1. “/user”是所有用户创建最高等级角色的守护者角色;通过ActorSystem.actorOf来创建。
  2. “/system”是所有系统创建最高等级角色的守护者角色,例如日志监听器或者在角色系统启动时通过配置自动部署的角色。
  3. “/deadLetters”是死信角色,所有发送给已停止的或不存在的角色的消息都被重新路由到此处(以尽力而为原则,消息也有可能在本地JVM中丢失)。
  4. “/temp”是所有短命的系统创建角色的守护者,例如那些用于实现ActorRef.ask的角色。
  5. “/remote”,其下所有角色的人工路径,这些角色的监管者都是远程角色引用。

 

为角色构建名称空间的需要,是源于一个非常简单的中心设计目标:在层次结构中万物皆角色,以及所有的角色以相同的方式运作。因此你不仅仅可以查找你创建的角色,你还可以查找系统守护者并给它发送消息(在这种情况下它会踏实的丢弃)。这个强大的原则,意味着没有奇怪的东西需要去记住,它使整个系统更加的统一和一致。

如果你想要了解更多关于角色系统最高等级结构的知识,请查看顶级监管者。

AKKA文档(java版)——用例与部署场景

原文:http://doc.akka.io/docs/akka/2.3.6/intro/deployment-scenarios.html 译者:吴京润

我如何使用与部署akka

有两种不同的使用akka的方式:

  • 作为一个库:作为一个web应用的类路径下的普通JAR使用,将它放在WEB-INF/lib。
  • 在一上主类中通过实例化ActorSystem作为一个独立的应用运行,或使用微内核 (Scala) /(Java)

akka作为一个库使用

如果你在构建一个web应用,这可能就是你想要的。在库模式下,通过向模块栈添加越来越多的模块有许多种使用akka的方式。

akka作为独立的微内核使用

akka还可以作为一个独立的微内核运行。更多信息见微内核 (Scala) / (Java) 

AKKA文档(java版)——hello world

原文:http://doc.akka.io/docs/akka/2.3.6/java/hello-world.html 译者:吴京润

基于actor在控制台打印这一知名问候的困难在Typesafe Activator教程中名为Akka Main in Java项目中已有介绍。

本教程说明了通用启动器类akka.Main,只接收一个命令行参数:应用的主actor类名。这个main方法将为运行actor创建基础设施,用来启动指定的主actor以及在主actor终止时为关闭整个应用做出安排

Typesafe Activator中有相同问题域的另一个名为Hello Akka!的教程讲述了更深入的akka基础知识。

AKKA文档(java版)—位置透明性

原文:http://doc.akka.io/docs/akka/2.3.6/general/remoting.html  译者:小鱼

2.6 位置透明性

前一章节描述了如何使用角色路径来实现位置透明性。这一个特性应该需要一些额外的说明,因为与之关联的术语“transparent remoting”(透明的远程处理)在编程语言、平台和技术中的用法是不一样的。

2.6.1 默认分布式
Akka中的所有事物被设计成用于分布式环境中:角色之间的交流都是纯信息传递,并且是同步的。这一成就已经被用于确保所有的功能在单个JVM或者在拥有数以百计的机器的集群中运行都同样有效。实现这一功能的关键在于从远程到本地的优化代替试图从本地到远程的范化。关于第二种方式注定要失败的详细讨论请访问this classic paper

2.6.2 透明方式被打破
对Akka适用的,不一定适用于使用它的应用,因为设计分布式执行会带来一些限制。最明显的一点就是所有通过电缆发送的消息都必须可序列化。虽然有一点不太明显的就是包括闭包在内的远程角色工厂,用来在远程节点创建角色(即Props内部)。
另一个结论是,要意识到所有交互都是完全异步的,它意味着在一个计算机网络中一条消息需要几分钟才能到达接收者那里(基于配置),而且可能比在单JVM中有更高丢失率,后者丢失率接近于0(还没有确凿的证据)。

2.6.3 如何远程使用?
我们持透明性的观点的限制是几乎没有关于Akka远程处理层的API:它纯粹是配置驱动的。根据前面章节讲述的原则去编写你的应用,然后在配置文件中指定角色子树的远程部署。你的应用可以用这种方式在不需要改动代码的情况下实现横向扩展。唯一的允许通过编程影响远程部署的API是Props,Props可以指定Deploy实例的属性,这种方式等同于通过配置文件部署(如果两种方式都用了,会以配置文件优先)。

2.6.4 点对点 VS 客户端-服务器端
Akka远程处理是一个在点对点模式下用来连接角色系统的通讯模块,而它是Akka集群的基础。远程处理是通过两个设计决策(相关的)作为导向来设计的:

  1. 相关系统之间的通信是对等的:如果一个系统A可以连接到系统B,那么系统B也一定可以独立的连接到系统A。
  2. 关于连接模式,通信系统的地位是对等的:没有一个系统是只能接受连接,也没有一个系统只能发起连接。

通过这些决策我们知道是不可能通过预定义角色(审校者注:原文为role,而不是文档通常出现的Actor)(违反假设1)和涉及网络地址转换或负载平衡器的设置(违反假设2)来安全的创建纯粹的client-server设置。

client-server的设置最好使用HTTP或者Akka I/O。

2.6.5 利用路由器纵向扩展标记点
除了能够在集群的不同节点上运行角色系统的不同部分,还可以通过支持并行的多角色子树扩展到更多的内核(想象成一个搜索引擎可以并行的处理不同的查询)。克隆可以不同的方式路由,例如轮询。唯一要做的就是开发人员需要声明一个特定的角色“withRouter”,然后,将会创建一个路由角色,该角色将产生许多可配置的期望类型的子角色并以配置指定的方式路由到它们。一旦声明了这样的一个路由器,它的配置可以自由的用配置文件覆盖,还可以与远程部署(的一些)子角色混合。更多内容请见Routing(Scala)和Routing(Java)。

AKKA文档(JAVA版)—派发器

原文地址  译者:Zhanggc

派发器

Akka MessageDispatcher 是维持 Akka Actor “运作”的部分, 可以说它是整个机器的引擎. 所有的MessageDispatcher 实现也同时是一个 ExecutionContext, 这意味着它们可以用来执行任何代码, 例如 Future.

 

缺省派发器

在没有为 Actor作配置的情况下,每个 ActorSystem 将有一个缺省的派发器。 缺省派发器是可配置的,缺省情况下是一个确定的default-executor的 Dispatcher。如果通过传递ExecutionContext 来创建ActorSystem ,在ActorSystem中,此ExecutionContext 将作为所有派发器的defalut-executor 。如果没有指定ExecutionContext,将后退到akka.actor.default-dispatcher.default-executor.fallback 的executor。缺省情况下的”fork-join-executor”,在大多数情况下拥有非常好的性能。

查找派发器

派发器实现ExecutionContext 接口,因此可以用来运行Future 调用 等待。

// for use with Futures, Scheduler, etc.
final ExecutionContext ex = system.dispatchers().lookup("my-dispatcher");

 

为角色指定派发器

在你想为Actor配置一个不同派发器而不是默认情况下,你需要做两样东西,首先是配置派发器:

my-dispatcher {
  # Dispatcher is the name of the event-based dispatcher
  type = Dispatcher
  # What kind of ExecutionService to use
  executor = "fork-join-executor"
  # Configuration for the fork join pool
  fork-join-executor {
    # Min number of threads to cap factor-based parallelism number to
    parallelism-min = 2
    # Parallelism (threads) ... ceil(available processors * factor)
    parallelism-factor = 2.0
    # Max number of threads to cap factor-based parallelism number to
    parallelism-max = 10
  }
  # Throughput defines the maximum number of messages to be
  # processed per actor before the thread jumps to the next actor.
  # Set to 1 for as fair as possible.
  throughput = 100
}

接着使用 “thread-pool-executor”:

my-thread-pool-dispatcher {
  # Dispatcher is the name of the event-based dispatcher
  type = Dispatcher
  # What kind of ExecutionService to use
  executor = "thread-pool-executor"
  # Configuration for the thread pool
  thread-pool-executor {
    # minimum number of threads to cap factor-based core number to
    core-pool-size-min = 2
    # No of core threads ... ceil(available processors * factor)
    core-pool-size-factor = 2.0
    # maximum number of threads to cap factor-based number to
    core-pool-size-max = 10
  }
  # Throughput defines the maximum number of messages to be
  # processed per actor before the thread jumps to the next actor.
  # Set to 1 for as fair as possible.
  throughput = 100
}

更多细节选项,请见默认派发器配置章节

ActorRef myActor =
  system.actorOf(Props.create(MyUntypedActor.class),
    "myactor");

 

akka.actor.deployment {
  /myactor {
    dispatcher = my-dispatcher
  }
}

一种代替部署配置方法是定义派发器在代码里面。如果在部署配置里面定义派发器则该值将被使用代替编码设置参数。

ActorRef myActor =
  system.actorOf(Props.create(MyUntypedActor.class).withDispatcher("my-dispatcher"),
    "myactor3");

 

注意:你在withDispatcher中指定的 “dispatcherId” 其实是配置中的一个路径. 所以在这种情况下它位于配置的顶层,但你可以把它放在下面的层次,用.来代表子层次,象这样: “foo.bar.my-dispatcher”。

派发器的类型

一共有4种类型的消息派发器:

  • Dispatcher
    • 这是个基于事件派发器,该派发器绑定一组角色到一个线程池中。如果没有一个明确定义,这将是一个默认派发器使用。
    • 可共享性: 无限制
    • 邮箱: 任何,为每一个Actor创建一个
    • 使用场景: 缺省派发器,Bulkheading
    • 底层使用: java.util.concurrent.ExecutorService specify using “executor” using “fork-join-executor”, “thread-pool-executor” or the FQCN of an akka.dispatcher.ExecutorServiceConfigurator
    • PinnedDispatcher
      • 该派发器致力于为每一个使用它的角色提供一个唯一的线程,例如:每一个角色将有自己的仅包含一个线程的线程池。
      • 可共享性: 无
      • 邮箱: 任何,为每个Actor创建一个
      • 使用场景: Bulkheading
      • 底层使用: 任何 akka.dispatch.ThreadPoolExecutorConfigurator缺省为一个 “thread-pool-executor”
      • CallingThreadDispatcher
        • 该派发器仅在当前线程上运行调用。此派发器不会创建任何新的线程,但可以使用来自不同的线程同时为相同的角色。请见 CallingThreadDispatcher for细节和限制
        • 可共享性: 无限制
        • 邮箱: 任何,每Actor每线程创建一个(需要时)
        • 使用场景: 测试
        • 底层使用: 调用的线程 (duh)

更多派发器配置例子

配置PinnedDispatcher:

my-pinned-dispatcher {
  executor = "thread-pool-executor"
  type = PinnedDispatcher
}

接着使用它:

ActorRef myActor = system.actorOf(Props.create(MyUntypedActor.class)
    .withDispatcher("my-pinned-dispatcher"));

 

注意:thread-pool-executor 配置按照上面my-thread-pool-dispatcher例子是不适用的。这是因为当使用PinnedDispatcher时候,每一个角色将有自己的线程池,线程池将只有一个线程。

注意:随着时间推移这将不保证一直使用相同线程,由于核心池超时用于PinnedDispatcher 在闲置角色情况下,降低资源使用。为了一直使用相同的线程,你需要添加 thread-pool-executor.allow-core-timeout=off到PinnedDispatcher配置中。

AKKA文档(java版)—类型化角色

原文:doc.akka.io/docs/akka/2.3.6/java/typed-actors.html  译者:小鱼      审校:吴京润

3.2 类型化角色

Akka的类型化角色是活动对象(Active Object)模式的实现。Smalltalk诞生的时候,默认的方法调用由异步派发代替同步操作。
类型化角色由2部分组成,包括一个公共的接口和实现,如果你有“企业级”Java的开发经验,这对你来说会非常熟悉。与普通的角色一样,你有一个外部的API(公共接口实例),将异步的方法调用委托给实现类的一个私有实例。

类型化角色对比角色的优势是你可以有一个静态的约定,而不需要去定义你自己的消息,不好的一面就是它会限制你能做什么和不能做什么,比如你不能使用become/unbecome。
类型化角色是利用JDK Proxies 来实现的,它提供一个非常简单的API去调用拦截方法。
注意
正如普通的非类型化角色一样,类型化角色每次处理一次调用。
3.2.1 什么时候使用类型化角色
类型化角色是角色系统和非角色代码之间的美好桥梁,因为它们允许你在外部编写正常的面向对象代码。把它们想象成门:它们实际上是公共部分和私有部分之间的接口,但你并不想你的房子有很多的门,不是吗?你可以通过this blog post查看更详细的讨论。
更多的背景:TypedActor很容易作为RPC被滥用,因此TypedActor并不是我们一开始想象中的那样,能够更加容易的去正确编写高可扩展的并发软件。我们要在合适的地方使用它们。

3.2.2 工具箱
在创建第一个类型化角色之前,我们先了解一下这个工具,掌握它的功能,它位于akka.actor.TypedActor。

//返回类型化角色的表达式

TypedActorExtension extension = TypedActor.get(system); //系统是一个ActorSystem对象</p>

//返回引用是否是一个类型化角色代理

TypedActor.get(system).isTypedActor(someReference);

 

//返回一个外部类型化角色代理的AKKA角色

TypedActor.get(system).getActorRefFor(someReference);

 

//返回当前ActorContext

//方法仅在一个TypedActor的方法实现中有效

ActorContext context = TypedActor.context();

 

//返回当前类型化角色的外部代理

//方法只在TypedActor的方法实现中有效

Squarer sq = TypedActor.<Squarer>self();

 

//返回一个类型化角色的上下文实例

//这意味着如果你使用它创建了其它的类型化角色实例

//它们将是当前这个类型化角色的子角色

TypedActor.get(TypedActor.context());

 

警告

        类型化角色和akka角色一样不暴露this引用,这一点很重要。你应该通过外部代理引用,它可以通过TypedActor.self来获得,这是你的对外身份标识,就像akka角色的对外身份标识是ActorRef一样。

3.2.3 创建类型化角色

创建类型化角色需要有一个以上的接口和一个实现接口的类。

假设入口如下所示:

import akka.actor.TypedActor;

import akka.actor.*;

import akka.japi.*;

import akka.dispatch.Futures;</p>

import scala.concurrent.Await;

import scala.concurrent.Future;

import scala.concurrent.duration.Duration;

import java.util.concurrent.TimeUnit;

 

import java.util.List;

import java.util.ArrayList;

import java.util.Random;

import akka.routing.RoundRobinGroup;

public class TypedActorDocTest {

    Object someReference = null;

    ActorSystem system = null;

 

    static public interface Squarer {

        void squareDontCare(int i); //fire-forget(审校者注:这个词怎么翻译?)

        Future<Integer> square(int i); //非阻塞send-request-reply

        Option<Integer> squareNowPlease(int i);//阻塞send-request-reply

        int squareNow(int i); //阻塞send-request-reply

    }

 

    static class SquarerImpl implements Squarer {

        private String name;

        public SquarerImpl() {

            this.name = "default";

        }

 

        public SquarerImpl(String name) {

            this.name = name;

        }

 

        public void squareDontCare(int i) {

            int sq = i * i; //Nobody cares

        }

        public Future<Integer> square(int i) {

            return Futures.successful(i * i);

        }

 

        public Option<Integer> squareNowPlease(int i) {

            return Option.some(i * i);

        }

 

        public int squareNow(int i) {

            return i * i;

        }

    }

 

    @Test public void mustGetTheTypedActorExtension() {

        try {

            //返回类型化角色的的表达式

            TypedActorExtension extension = TypedActor.get(system); //系统是一个ActorSystem实例

 

            //返回引用是否是一个类型化角色代理

            TypedActor.get(system).isTypedActor(someReference);

 

            //返回类型化角色代理的AKKA角色

            TypedActor.get(system).getActorRefFor(someReference);

 

            //返回当前ActorContext

            // 方法只在TypedActor方法实现内部有效

            ActorContext context = TypedActor.context();

 

            //返回当前类型化角色的外部代理

            // 方法只在TypedActor方法实现内部有效</pre>

            Squarer sq = TypedActor.<Squarer>self();

 

            //返回类型化角色的上下文实例

            //这意味着如果你用它创建了其它类型化角色

            //它们就是当前类型化角色的子角色

            TypedActor.get(TypedActor.context());

        } catch (Exception e) {

            //dun care

        }

     }

     @Test public void createATypedActor() {

         try {

             Squarer mySquarer = TypedActor.get(system).typedActorOf(

                 new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class));

             Squarer otherSquarer = TypedActor.get(system).typedActorOf(

                 new TypedProps<SquarerImpl>(Squarer.class, new Creator<SquarerImpl>() {

                     public SquarerImpl create() { return new SquarerImpl("foo"); }

             }),"name");

 

             mySquarer.squareDontCare(10);

             Future<Integer> fSquare = mySquarer.square(10); //A Future[Int]

             Option<Integer> oSquare = mySquarer.squareNowPlease(10); //Option[Int]

             int iSquare = mySquarer.squareNow(10); //Int

 

             assertEquals(100, Await.result(fSquare, Duration.create(3, TimeUnit.SECONDS)).intValue());

             assertEquals(100, oSquare.get().intValue());

             assertEquals(100, iSquare);

 

             TypedActor.get(system).stop(mySquarer);

             TypedActor.get(system).poisonPill(otherSquarer);

        } catch(Exception e) {

             //忽略

        }

    }

 

    @Test public void createHierarchies() {

        try {

            Squarer childSquarer = TypedActor.get(TypedActor.context()).typedActorOf(

                new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class)

            );

            //Use "childSquarer" as a Squarer

        } catch (Exception e) {

            //dun care

        }

    }

 

    @Test public void proxyAnyActorRef() {

        try {

            final ActorRef actorRefToRemoteActor = system.deadLetters();

            Squarer typedActor = TypedActor.get(system).typedActorOf(

            new TypedProps<Squarer>(Squarer.class),actorRefToRemoteActor);

            //Use "typedActor" as a FooBar

        } catch (Exception e) {

            //dun care

        }

    }

 

    interface HasName {

        String name();

    }

 

   class Named implements HasName {

        private int id = new Random().nextInt(1024);

        @Override public String name() { return "name-" + id; }

    }

 

    @Test public void typedRouterPattern() {

       try {

           // prepare routees

           TypedActorExtension typed = TypedActor.get(system);

           Named named1 = typed.typedActorOf(new TypedProps<Named>(Named.class));

           Named named2 = typed.typedActorOf(new TypedProps<Named>(Named.class));

 

           List<Named> routees = new ArrayList<Named>();

           routees.add(named1);

           routees.add(named2);

 

           List<String> routeePaths = new ArrayList<String>();

           routeePaths.add(typed.getActorRefFor(named1).path().toStringWithoutAddress());

           routeePaths.add(typed.getActorRefFor(named2).path().toStringWithoutAddress());

 

           // prepare untyped router

           ActorRef router = system.actorOf(new RoundRobinGroup(routeePaths).props(), "router");

 

          //准备类型化代理,向“router”转发方法调用消息

          Named typedRouter = typed.typedActorOf(new TypedProps<Named>(Named.class), router);

 

           System.out.println("actor was: " + typedRouter.name()); // name-243

           System.out.println("actor was: " + typedRouter.name()); // name-614

           System.out.println("actor was: " + typedRouter.name()); // name-243

           System.out.println("actor was: " + typedRouter.name()); // name-614

 

           typed.poisonPill(named1);

           typed.poisonPill(named2);

           typed.poisonPill(typedRouter);

       } catch (Exception e) {

           //dun care

       }

    }

}

接口的例子:

    public interface Squarer {

        //类型化的角色接口方法 ...

    }

接口的实现类:

class SquarerImpl implements Squarer {

    private String name;

    public SquarerImpl() {

        this.name = "default";

    }

 

    public SquarerImpl(String name) {

        this.name = name;

    }

 

    //类型化的角色方法实现 ...

}

创建Squarer的类型化角色最简单的方式如下:

Squarer mySquarer = TypedActor.get(system).typedActorOf(new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class));

      第一种类型是代理类型,第二种是代理类型的实现。如果你需要去调用一个特殊的构造器,你可以这样做:

Squarer otherSquarer = TypedActor.get(system).typedActorOf(new TypedProps<SquarerImpl>(Squarer.class,new Creator<SquarerImpl>() {

    public SquarerImpl create() { return new SquarerImpl("foo"); }

}),"name");

 既然提供一个Props,你可以指定使用哪一个调度程序,应该使用缺省的timeout或者别的。目前,Squarer没有定义任何方法,我们可以添加如下这些方法。

public interface Squarer {

    void squareDontCare(int i); //fire-forget

    Future<Integer> square(int i); //non-blocking send-request-reply

    Option<Integer> squareNowPlease(int i);//blocking send-request-reply

    int squareNow(int i); //blocking send-request-reply

}

那好,现在我们可以调用这些方法了,不过他们需要在SquarerImpl中实现。

class SquarerImpl implements Squarer {

    private String name;

    public SquarerImpl() {

        this.name = "default";

    }

 

    public SquarerImpl(String name) {

        this.name = name;

    }

 

    public void squareDontCare(int i) {

        int sq = i * i; //Nobody cares

    }

 

    public Future<Integer> square(int i) {

        return Futures.successful(i * i);

    }

 

    public Option<Integer> squareNowPlease(int i) {

        return Option.some(i * i);

    }

 

    public int squareNow(int i) {

       return i * i;

    }

}

很好,现在我们已经有一个接口和它的实现类,并且知道怎么去创建一个类型化角色了,让我们了解下这些方法。

3.2.4 方法调度语义

         方法返回:

        1. void会被fire-and-forget语义调度,和ActorRef.tell完全一样

        2. scala.concurrent.Future<?>会使用send-request-reply语义,和ActorRef.ask完全一样。

        3. akka.japi.Option<?>会使用send-request-reply语义,但会堵塞等待一个应答,并且如果在timeout内没有回复,就会返回akka.japi.Option.None,或者,相反的返回akka.japi.Option.Some<?>。此调用过程中被抛出的任何异常都将被重新抛出。

        4. 任何别的类型值会使用send-request-reply语义,但会阻塞等待一个回答,如果在一个timeout内抛出异常或者在调用过程中出现重新抛异常的情况,就会抛出java.util.concurrent.TimeoutException。注意,基于Java异常和反射机制,一个TimeoutException会包装在一个java.lang.reflect.UndeclaredThrowableException里,除非接口方法明确的描述TimeoutException作为一个受检异常抛出。

3.2.5 消息和不可变性

         虽然Akka不能强制转换类型化角色方法的参数为不可变的,但是我们强烈建议把参数设置为不可变的。

3.2.5.1 单向(One-way)消息发送

mySquarer.squareDontCare(10);

就像上面这么简单,方法会在另一个线程里异步的执行。

3.2.5.2 双向(Request-reply)消息发送

Option<Integer> oSquare = mySquare.squareNowPlease(10);//Option[Int]

如果需要,阻塞的时长可以配置类型化角色的Props的timeout。如果超时,它会返回None。

int iSquare = mySquarer.squareNow(10);//Int

如果需要,阻塞的时长可以配置类型化角色的Props的timeout。如果超时,它会抛出一个java.util.concurrent.TimeoutException。这里需要注意一下,通过Java的反射机制,这样一个TimeoutException会被包装在一个java.lang.reflect.UndeclaredThrowableException中,因为接口方法没有明确描述TimeoutException作为一个受检异常抛出。为了直接得到TimeoutException,可以在接口方法中添加throws java.util.concurrent.TimeoutException。

3.2.5.3 (Request-reply-with-future) 消息发送

Future<Integer> fSquare = mySquarer.square(10);//一个Future对象[Int]

这个调用是异步的,并且future的返回可以用于异步成分。

 

3.2.6 停止类型化角色

一旦Akka的类型化角色被Akka角色阻塞,当不再需要它们,必须被停掉。

TypedActor.get(system).stop(mySquarer);

</span></span>

这个异步的方法会尽快的停掉类型化角色关联的代理。

TypedActor.get(system).poisonPill(otherSquarer);

这个异步的方法会在所有调用都完成之后停掉类型化角色关联的代理。

 

3.2.7 类型化角色层次结构

既然你可以通过传递一个ActorContext来获得一个上下文的类型化角色,你可以通过在它上面调用typeActorOf来创建子类型化角色。

Squarer childSquarer = TypedActor.get(TypedActor.context()).typedActorOf(

    new TypedProps<SquarerImpl>(Squarer.class, SquarerImpl.class)

);

//Use "childSquarer" as a Squarer

你可以通过常规的Akka角色,把UntypedActorContext作为TypedActor.get的输入参数创建一个子类型化角色。

 

3.2.8 监管者策略

通过你的类型化角色实现类实现TypedActor.supervisor。你可以定义策略去监管子角色,就像监管与监控(Supervision and Monitoring) and 容错(Fault Tolerance)中描述的一样。

 

3.2.9 接收任意的消息

如果你的TypedActor的实现类继承akka.actor.TypedActor.Receiver,所有非方法调用的消息都会传递到onReceive方法。

这允许你处理DeathWatch的Terminated消息和别的类型的消息,例如当与非类型化角色进行交互的场景。

 

3.2.10 生命周期回调

通过你的类型化角色实现类实现如下任何一个或所有的方法:

  • TypedActor.PreStart
  • TypedActor.PostStop
  • TypedActor.PreRestart
  • TypedActor.PostRestart

你可以用钩子方法连接到你的类型化角色的生命周期。

 

3.2.11 代理

你可以使用带TypedProps和Actor引用参数的 typedActorOf以代理的方式将ActorRef引用转换成类型化角色。如果你想和其他机器上的TypedActor进行远程交互,只要把ActorRef传给typedActorOf即可。

 

3.2.12 查找和远程处理

既然TypedActor是基于Akka Actor的,你可以用typedActorOf去代理有可能在远程节点上的ActorRef。

Squarer typedActor = TypedActor.get(system).typedActorOf(

    new TypedProps<Squarer>(Squarer.class),actorRefToRemoteActor);

//Use "typedActor" as a FooBar

3.2.13 类型化路由模式

有时候你想在多个角色之间传递消息。在Akka中最简单的实现方法是用一个路由(Router),它可以实现一个特定的路由逻辑,例如最小邮箱(smallest-mailbox)或者一致性哈希(consistent-hashing)等等。

路由没有直接提供给类型化角色,但可以通过利用一个非类型化路由和类型化代理来实现它。为了展示这一点让我们创建一些类型化角色并给他们指派随机的id,然后我们会看到路由把消息发送给了不同的角色:

@Test public void typedRouterPattern() {

    try {

        // prepare routees

        TypedActorExtension typed = TypedActor.get(system);

 

        Named named1 = typed.typedActorOf(new TypedProps<Named>(Named.class));

        Named named2 = typed.typedActorOf(new TypedProps<Named>(Named.class));

 

        List<Named> routees = new ArrayList<Named>();

        routees.add(named1);

        routees.add(named2);

 

        List<String> routeePaths = new ArrayList<String>();

        routeePaths.add(typed.getActorRefFor(named1).path().toStringWithoutAddress());

        routeePaths.add(typed.getActorRefFor(named2).path().toStringWithoutAddress());

 

        // 准备类型化路由

        ActorRef router = system.actorOf(new RoundRobinGroup(routeePaths).props(), "router");

 

        // 准备类型化代理,向路由转发MethodCall消息

        Named typedRouter = typed.typedActorOf(new TypedProps<Named>(Named.class), router);

 

        System.out.println("actor was: " + typedRouter.name()); // name-243

        System.out.println("actor was: " + typedRouter.name()); // name-614

        System.out.println("actor was: " + typedRouter.name()); // name-243

        System.out.println("actor was: " + typedRouter.name()); // name-614

 

        typed.poisonPill(named1);

        typed.poisonPill(named2);

        typed.poisonPill(typedRouter);

 

    } catch (Exception e) {

        //dun care

    }

}

为了在这样的角色实例中间轮询,你可以创建一个简单的非类型化路由,并用一个TypedActor实现这个路由的外观模式,就像下面的例子展示的。这样会起作用是因为类型化角色利用和普通角色相同的通讯机制,以及它们的方法调用被转化为MethodCall消息。

// prepare routees

TypedActorExtension typed = TypedActor.get(system);

 

Named named1 = typed.typedActorOf(new TypedProps<Named>(Named.class));

 

Named named2 = typed.typedActorOf(new TypedProps<Named>(Named.class));

 

List<Named> routees = new ArrayList<Named>();

routees.add(named1);

routees.add(named2);

 

List<String> routeePaths = new ArrayList<String>();

routeePaths.add(typed.getActorRefFor(named1).path().toStringWithoutAddress());

routeePaths.add(typed.getActorRefFor(named2).path().toStringWithoutAddress());

 

// prepare untyped router

ActorRef router = system.actorOf(new RoundRobinGroup(routeePaths).props(), "router");

 

// prepare typed proxy, forwarding MethodCall messages to `router`

Named typedRouter = typed.typedActorOf(new TypedProps<Named>(Named.class), router);

 

System.out.println("actor was: " + typedRouter.name()); // name-243

System.out.println("actor was: " + typedRouter.name()); // name-614

System.out.println("actor was: " + typedRouter.name()); // name-243

System.out.println("actor was: " + typedRouter.name()); // name-614

AKKA文档(Java版)—建立有限状态机角色

原文地址  译者:Zhanggc

建立有限状态机角色

概述

有限状态机模式在Erlang design principles里面被很好描述出来.简而言之,它可以被视为一组关系:
State(S) x Event(E) -> Actions (A), State(S’)

这些关系描述为:

如果我们在状态S 和 时间E 发生,我们应该执行动作A 与转换到状态S’.

而Scala 程序语言使构建一个良好内部DSL(领域特定语言)成为可能,后者用于规划有限状态机(请见FSM)。对于用同样方法,由于Java语法冗长不适合构建。本章节介绍通过自身训练效实现相同关注点分离方法。

状态应该如何处理

FSM角色实现引用的所有可变字段(或可传递的可变数据结构)应该被收集在一个地方及仅通过使用小粒度的的定义良好的一组方法来改变。要做到这一点的一种实现方法是集合所有可变状态在一个超类中,并且保持其的私有及为改变其提供保护方法。

import java.util.ArrayList;
import java.util.List;
import akka.actor.ActorRef;

 

public abstract class MyFSMBase extends UntypedActor {
 
  /*
   * This is the mutable state of this state machine.
   */
  protected enum State {
    IDLE, ACTIVE;
  }
 
  private State state = State.IDLE;
  private ActorRef target;
  private List<Object> queue;
 
  /*
   * Then come all the mutator methods:
   */
  protected void init(ActorRef target) {
    this.target = target;
    queue = new ArrayList<Object>();
  }
 
  protected void setState(State s) {
    if (state != s) {
      transition(state, s);
      state = s;
    }
  }
 
  protected void enqueue(Object o) {
    if (queue != null)
      queue.add(o);
  }
 
  protected List<Object> drainQueue() {
    final List<Object> q = queue;
    if (q == null)
      throw new IllegalStateException("drainQueue(): not yet initialized");
    queue = new ArrayList<Object>();
    return q;
  }
 
  /*
   * Here are the interrogation methods:
   */
  protected boolean isInitialized() {
    return target != null;
  }
 
  protected State getState() {
    return state;
  }
 
  protected ActorRef getTarget() {
    if (target == null)
      throw new IllegalStateException("getTarget(): not yet initialized");
    return target;
  }
 
  /*
   * And finally the callbacks (only one in this example: react to state change)
   */
  abstract protected void transition(State old, State next);
}

上面方法好处是状态改变可以表现在一个中心位置之上,当添加到FSM机器时,这使得它不可能忘记插入代码对于状态转变的反应。

消息集束器的例子

上面显示的基类被设计支持一个类似例子作为Scala FSM 文档:一个接收和排队消息的角色,分批交付给一个可配置的目标角色。涉及的消息是:

public final class SetTarget {
  final ActorRef ref;
 
  public SetTarget(ActorRef ref) {
    this.ref = ref;
  }
}
 
public final class Queue {
  final Object o;
 
  public Queue(Object o) {
    this.o = o;
  }
}
 
public final Object flush = new Object();
 
public final class Batch {
  final List<Object> objects;
 
  public Batch(List<Object> objects) {
    this.objects = objects;
  }
}

该角色仅有两个状态 IDLE 和 ACTIVE,使他们处理相当直接在来自于基类的具体角色。

import akka.event.LoggingAdapter;
import akka.event.Logging;
import akka.actor.UntypedActor;

 

public class MyFSM extends MyFSMBase {
 
  private final LoggingAdapter log =
    Logging.getLogger(getContext().system(), this);
 
  @Override
  public void onReceive(Object o) {
 
    if (getState() == State.IDLE) {
 
      if (o instanceof SetTarget)
        init(((SetTarget) o).ref);
 
      else
        whenUnhandled(o);
 
    } else if (getState() == State.ACTIVE) {
 
      if (o == flush)
        setState(State.IDLE);
 
      else
        whenUnhandled(o);
    }
  }
 
  @Override
  public void transition(State old, State next) {
    if (old == State.ACTIVE) {
      getTarget().tell(new Batch(drainQueue()), getSelf());
    }
  }
 
  private void whenUnhandled(Object o) {
    if (o instanceof Queue && isInitialized()) {
      enqueue(((Queue) o).o);
      setState(State.ACTIVE);
 
    } else {
      log.warning("received unknown message {} in state {}", o, getState());
    }
  }
}

这里技巧是分解出像whenUnhandled 与transition 这样的基本功能,以便获得一些定义良好方面在对于改变或插入记录做出的反应。

状态中心 VS 事件中心

在上面例子中,状态和事件的主观复杂性是大致相等的,使得它看起来是是否选择主派发的问题;在这个例子中基于状态的派发器被选中。取决于如何均衡构建状态与事件可行模型,首先处理不同事件,接着辨别状态,这中做法可能是更实际。一个例子是一个状态机,它具有多种内部状态,但只处理很少不同事件。

AKKA文档(java)——角色系统

原文:http://doc.akka.io/docs/akka/2.3.6/general/actor-systems.html  译者:吴京润

角色是封装了状态与行为的对象,它们通过交换放入接收者信箱的消息实现两两之间的通讯。从某种意义上说,角色是最严格的面向对象编程,不过最好还是把它们当作人来看待:当用角色为一个方案建模时,想象有一群人,并给他们分配了任务,他们在一个组织结构中发挥职能作用,并想象如何做到故障升级(就像在不需要考虑实际利益的情况下与人打交道,也就是说我们不需要关心他们的情绪变化或道德问题)。这样的结果可以充当构建软件的心理脚手架。

注意:一个角色系统是一个会分配1…N个线程的重量级结构,因此为每个逻辑上的应用创建一个角色系统即可。

层次结构

就像在一个经济组织内,角色自然形成了层次结构。一个程序中监控特定功能的角色,可能想把自己的任务分解为更小,更容易管理的片段。基于这一目的,它启动了子角色,并管理它们。本节我们关注基本概念,更多细节在这里。惟一的前提是每个角色都有一个管理者,也就是它的创建者。

角色系统的典型特性是任务的拆分与委派,直到任务拆分的足够小。这样做,不只任务自己有清晰的结构,而且作为结果产生的角色也可以决定它们可以处理以及如何处理哪些消息,还有故障如何解决等等。如何一个角色遇到不能处理的情况,它会向管理者发送一条失败消息,去寻求帮助。这种递归结构允许在合适的级别处理故障。

将这种思想与分层的软件设计比较,后者更容易演变为防御性编程,以开发没有故障的软件为目标:程序如何与正确的对象(译者注:原文为person,但本人认为此处应该是指程序之间或模块之间的交互方式)交互;一个更好的方案是如何发现故障,而不是把一切“都藏在地毯下面”。

现在设计这样一个系统的困难之处在于如何决定谁应该管理什么。当然没有一个最好的方案,但是有一些准则可能会有帮助:

  • 如果一个角色管理其它角色的工作,比如通过传递子任务,这个管理者就应当管理子角色。理由是管理者知道会发生哪些故障以及如何处理它们。
  • 如果一个角色持有数据(也就是说它的状态要避免丢失),这一角色应当找出它监管的所有子角色处理的任何可能有危险的子任务,并适当处理这些子角色的故障。根据请求的性质,为每个请求创建一个新的子角色可能是最好的方案,这样简化了为收集应答的状态管理。这种模式来自Erlang,被称做错误内核模式。
  • 如果一个角色依赖于另一个才能履行它的职责,它应当监视其它角色的活跃度,并在收到终止通知时行动。这与监管不同,监视方对监管策略没有影响,而且应当注意到的是,单纯的功能性依赖不是决定是否要在层次结构的什么位置放置一个子角色的标准。

对于这些规则,当然总有例外;但是不论你应当有充足的理由决定遵守这些规则还是破坏它们。

配置环境

作为一个角色的协作集合的角色系统是管理共享设施的天然单元,比如调度服务、配置、日志等。拥有不同配置的多个角色系统可能无碍的共存于同一个JVM中,在Akka内部没有全局共享状态。还有一点,角色系统之间通讯的透明性——单节点内部或跨网络节点的通讯——可以构建功能层次的模块。

角色最佳实践

  1. 角色应该像好同事:高效的完成工作而且不打扰他人,避免占用资源。翻译成编程行为就是以事件驱动的方式处理事件生成响应(或更多请求)。除非是不可避免的,否则角色不应被外部的实体阻塞(也就是占用着一个线程的被动等待)——可能是一把锁,一个网络套接字等等。对于不可避免的情况请见下文。
  2. 不在可变对象之间传递可变对象。为了确保这一点,最好不改变消息。如果角色的封装是因向外暴露自己的可变状态而遭到破坏,你就退回到了通常的所有Java并发编程缺陷的境地。
  3. 角色是状态和行为的容器,接受这一点意味着不常发送消息内的行为(可能使用Scala闭包很有诱惑力)。风险之一就是不小心就在角色之间共享了可变状态,而这一点违反了角色模型的做法破坏了基于角色编程的良好体验的一切特性。
  4. 顶级角色处于你的错误内核(Error Kernel)的最深处,所以尽量不要创建它们,喜欢真正的分层系统。这一点对故障处理有好处(同时考虑到配置与性能的粒度),还降低了监护人角色的重要性(译者注:原文没有重要性一词,此处原文为strain,即血统,本人认为这句话原义是指监控护人角色的家族成员数量,后面半句指出过度使用这种角色会形成单点争用),如果过度使用这就形成了单点争用。

阻塞需要仔细的管理

在某些情况下阻塞操作不可避免,也就是令一个线程进入不定时间的休眠,等待一个外部事件发生。传统的RDBMS驱动或消息API就是例子,深层的原因通常是幕后发生了(网络)I/O。面对这一点,你可能倾向于仅仅用Future对象包装这个阻塞调用,并用跟此对象代替直接与IO之间的交互,但是这个策略实在是太简单了:当应用的负载增加时,你很可能会发现瓶颈所在,或耗尽内存,或线程过多。

下面是“阻塞问题”恰当方案的不完全清单:

  • 在一个角色(或由路由器管理的角色组【Java,Scala】)内部执行阻塞调用,确保配置一个足够大的线程池专门用于这一目的。
  • 在一个Future对象内执行阻塞调用,确保此类调用在任意时间点内的上限(无限制的提交任务会耗尽你的内存或线程数)。
  • 在一个Future对象内执行阻塞调用,提供一个线程池,这个线程池的线程上限要与运行应用程序的硬件相符。
  • 专门用一个线程管理一组阻塞资源(比如说NIO选择器驱动多个通道),以角色消息的形式调度事件。

第一种方案可能尤其适用于单线程模型,比如数据库句柄传统上一次只能执行一个查询,并使用内部同步方式确保这一点。一个常见的模式是为N个角色创建一个路由器,每个角色包装一个数据库连接,而查询是发送到路由器的。数字N必须是最大吞吐量,而数字大小取决于在什么样的硬件上部署了哪种数据库管理系统(DBMS)。

注意

配置线程池的工作最好委托给AKKA,简单配置在application.conf文件里并通过一个ActorSystem对象实例化【JAVA,Scala】。

AKKA文档(java)——术语,概念

原文:http://doc.akka.io/docs/akka/2.3.6/general/terminology.html 译者:吴京润

本章我们试图建立一个通用的术语列表,用来定义有关并发和分布式系统的坚实的基础,而这也是akka的目标。请注意,在这些术语当中许多并没有一致的定义。我们简单的寻求在akka文档范围内使用的工作定义。

并发与并行

并发与并行是相关的概念,但是也有很多细节上的差异。并发意味着两个或更多的任务正在取得进展,即使它们不是同时执行的。例如,可以用时间片的方式实现这一点,每个任务在时间片内执行一小部分,并与其它任务的切片混合执行。并行的出现使任务实现了真正的同时执行。

异步与同步

如果方法的调用者在方法返回或抛出异常前不能有任何进展,这个方法调用就被认为是同步的。然而异步调用允许调用者可以取得有限的进展,调用的方法完成时会通过其它机制(可能是注册的回调、Future对象或一条消息)向调用者发送信号。

一个同步API可能使用阻塞方式实现,但这不是必须的。对于一个CPU密集型任务可能有类似的阻塞行为。一般情况下,异步API是更好的选择,因为它们确保任务可以取得进展。角色是天生异步的,一个角色能够在发送一条消息后不用等待实际的传输行为而继续执行。

非阻塞与阻塞

我们谈论的阻塞是指一个线程的延迟可以无限期延迟其它一些线程。一个不错的例子是一个线程以互斥的方式独占一项资源。如果一个线程无限期的(比如不小心运行了一个无限循环)持有这项资源,而其它线程因等待该资源而无法继续运行。与此相反,非阻塞意味着没有线程可以无限期的延迟其它线程。

非阻塞操作比阻塞操作要优先选择,当系统中包含阻塞操作时,系统整体的进度就不能得到一般性的保证。

死锁、饥饿与活锁

死锁的出现是因为多个参与者同时互相等待对方到达某个特定状态进而得以继续,然而它们都因为其它参与者不能到达这个特定状态(一个“第二十二条军规”问题)而不能继续,所有相关子系统都不能被中断。死锁与阻塞紧密相关,因为一个参与者线程有能够无限期延迟其它线程的必要。

发生了死锁时,没有参数者可以继续执行,而饥饿恰好与之相反,当有参与者可以执行时,可能会有一个或更多得不到执行机会。典型场景是一个简单调度算法问题挑选高优先级任务。如果持续传入足够多的高优先级任务,低优先级任务永远也不会执行。

活锁类似死锁,没有参与者可以继续执行。所不同的是,不是在等待其它参与者的进度,而是参与者自己不断改变自己的状态。一个示例场景是两个参与者有两个相同的可用资源。它们都试图获得资源,但是它们还会检查是否有其它参与者也需要这个资源。当资源被其它参与者请求时,它们尝试得到这个资源的其它实例。在不走运的时候,可能会发生两个竞争者在两个资源之间反弹,永远也不占有它们,一直迁就于其它参与者。

竞态条件

我们说的竞态条件是指有关一组事件的顺序的假设可能会受外部不确定性因素的影响。竞态条件经常发生在多线程拥有一个共享可变状态,而线程交替操作引发的意外行为。虽然这是一种常见情况,共享状态也不必出现竞态条件。一个例子是,客户端向服务器发送无序数据包(UDP数据报就是这样的例子) P1,P2。数据包可能经由不同的网络路径,服务器可能先收到P2再收到P1。如果消息不包含有关它们的发送顺序的信息,服务器就不能确定数据包的顺序了。依赖于数据包的意义就可能引发竞态条件。

注意

AKKA只保证在一对给定角色之间传递的消息始终保持消息传递的顺序。见消息传递的可靠性。

非阻塞担保(进展状况)

在前面的章节中,因为一些原因而不推荐阻塞,包括死锁的危险,降低系统吞吐量。接下来的部分我们从不同程度上讨论多种非阻塞属性。

无等待

一个无等待的方法是指每次调用确保在有限步骤内结束。如果一个方法是有界无等待的,它的步数就有一个上限。

基于这个定义,无等待方法永远不阻塞,因此也不会死锁,因为每一部分都可以在有限步数以后继续(当调用结束时),无等待方法也不会发生饥饿。

无锁定

无锁定属性比无等待要弱。发生无锁定的调用时,方法常常在有限步骤内完成。这一定义意味着无锁定的调用不会发生死锁。另一方面,确保一些方法在有限步内完成不足以确保所有调用最终都会完成。换句话说,元锁定不足以确保不会发生饥饿。

无阻塞

无阻塞是本文讨论的比无锁定还要弱的保证。一个无阻塞的方法调用是指如果有一个隔离执行的时间点(其它线程不执行任何步骤,比如挂起),它在有限步数内结束。所有无锁定的对象都是无阻塞的,但是反过来通常不成立。

乐观并发控制(OCC方法通常是无阻塞的。OCC的做法是,每一个参与者试图操作共享对象,但是如果一个参与者探测到与其它参与者冲突,它就回滚所有个性,并按照某些调度策略再次尝试。如果某个时间点上有且只有一个参与者在尝试,操作就会成功。

推荐文献

  • 多处理器编程的艺术(The Art of Multiprocessor Programming),M. Herlihy与N Shavit, 2008. ISBN 978-0123705914
  • Java并发编程实践(Java Concurrency in Practice),B. Goetz, T. Peierls, J. Bowbeer, D. Holmes与D. Lea. 2006. ISBN 978-0321349606

akka文档(java)——akka的用例示例

原文:http://doc.akka.io/docs/akka/2.3.6/intro/use-cases.html   译者:吴京润

我们看到akka正被许多大型组织所采用,已被广泛应用于投资与商业银行业务,零食与社交媒体,虚拟仿真,游戏与博彩,数据分析等等行业。任何需要高吞吐量和低延迟的系统都是使用akka的良好候选者。

这里有一些良好的关于akka用例的讨论以及在生产环境的使用评论。

下面是一些已将akka部署到生产环境的领域

事务处理(在线游戏,金融、银行,贸易,统计,博彩,社会化媒体,电信)

纵向扩展,横向扩展,容错/HA

后端服务 (任何行业,任何应用)

REST服务, SOAP, Cometd, WebSockets等,充当信息枢纽/集成层纵向扩展 、横向扩展,容错/HA

并发/并行性(任何应用)

简单正确的使用与理解akka仅仅是将它的jar添加到你的JVM工程(使用Scala、Java、Groovy或JRuby)

虚拟仿真

主/从,计算风格,MapReduce等

批处理(任何行业)

Camel与批量数据源整合,角色对工作负载分而治之

通讯枢纽(电信、网络媒体、移动媒体)

纵向扩展,横向扩展,容错/HA

游戏与博彩(MOM(消息中间件),网络游戏,博彩)

纵向扩展,横向扩展,容错/HA

商业智能/数据挖掘/通用运算

纵向扩展,横向扩展,容错/HA

复杂的事件流处理

纵向扩展,横向扩展,容错/HA

 

AKKA文档(java版)