.NET Orleans与Akka比较 - akka-meta


两个项目在主要重点上的区别:

  • 奥尔良的主要重点是简化分布式计算,并允许非专家编写高效,可扩展和可靠的分布式服务。(banq注:类似EJB或JMS做法,试图隐藏网络通信的复杂性,与EJB的区别是,EJB之间通信是类似Akka之间的异步通信方式)
  • Akka是用于构建分布式系统的工具包,它不仅提供了强大的功能,而且还暴露了该领域的内在复杂性。

另一个区别是设计方法:
  • 对于奥尔良来说,指导性问题是“对于非专家而言,最自然,最容易的默认行为是什么?” 第二个问题才是专家如何做出自己的决定。(banq注:易用性高于灵活性)
  • Akka的指导性问题是“我们可以不妥协地提供最小的抽象是什么?” 这意味着对我们而言,“良好的默认设置”不是由用户期望的结果决定的,而是我们认为用户一旦理解了抽象就将对他们的程序进行推理时最有用的东西。

生命周期
  • 奥尔良Grains 没有生命周期,无法启动或停止。因此,它们也不会失败并无法重新启动,因此Orleans不提供用于软件故障处理的工具,而故障处理工具主要着重于从硬件崩溃中恢复。
    另一方面,“Grains 激活”确实具有生命周期和相应的生命周期挂钩,程序员可以使用它们对激活或停用做出反应。
  • Akka Actor实现了完整的模型,包括定义的生命周期开始和结束,这些是显式的操作。持久参与者支持将逻辑计算单元的生命周期扩展到正在运行的流程实例的生命周期之外。重新启动Actor为自动服务恢复提供了强大的手段。

自动创建
  • Orleans Grains会在需要时自动创建,这意味着激活初始化应谨慎对待其具有的外部可见效果-具有持久效果的初始化活动应设为客户端调用的显式接口方法。自动创建使用户不必考虑需要创建Grains。
  • Akka演员由其父母明确创建,并实施强制性的父母监督。这样可以精确控制何时执行初始化操作以及创建哪个确切的actor类。

虚拟Actor空间
  • 在Orleans ,每种类型的Grain对应于一个几乎无限的Grain实例空间,这些实例在概念上都从宇宙的开始到其结束都存在。类似于虚拟和物理内存的方式解释了与实现Grains的物理Actor的关系,其中换入和换出对应于Grain的激活/停用(在其他方面,类推并不成立,例如给定类型的所有Grains始终存在,而虚拟内存需要显式映射)。
  • Akka的显式生命周期管理要求所有当前正在运行的Actor必须在过去已显式启动,并且将来必须显式停止。诸如ClusterSharding之类的更高级别的抽象提供了加入类似于Orleans的模型的能力,在该模型中,实例可以通过实例的逻辑名称而不是具体的ActorRef进行寻址,但是用于访问实例的API由执行所需查找和初始化的其他Actor提供代表用户。

自动Actor设置和负载平衡
  • Orleans Grains部署在可以跨越多个群集节点的筒仓silor上,并且用户对其精确放置或与负载有关的移动没有​​直接影响。这意味着使用弹性供应的资源是全自动的,并已内置到模型中。
  • 弹性部署和负载平衡是Akka用户通过将ClusterSharding或支持群集的路由器与远程部署一起使用而选择的功能。否则,将在给定的节点上显式创建Actor(可以使用配置文件来影响Actor,以便允许操作人员而非程序员进行部署决策)。

虚拟Actor
  • Orleans Grains通过其标称类型和128位GUID,长整数,字符串或这三者的组合来标识。
  • Akka Actor由其ActorRef标识,该ActorRef由ActorPath(包括物理网络位置及其所有祖先的名称)和UID(一个32位整数,可消除同一ActorPath上的不同化身)组成。

Actor和Grains不与同伴共享内存,两者都只能通过消息传递进行通信。

自动实例化

  • Orleans Grains可能在任何给定时间都有一个或多个激活(活动实例),具体取决于部署模式:如果为谷物类型启用了自动横向扩展(请参见下文),那么程序员将不得不考虑可能存在多个副本使用相同的标识符运行。
  • Akka ActorRef是指必须事先实例化的Actor(否则将没有ActorRef,而只有ActorPath可用)并且可能已经终止。永远不会有两个Actor与同一个ActorRef一起运行。使用ClusterSharding时,在任何给定时间点,每个逻辑Actor名称当前可能正好运行着零个实例或一个实例(除非用户明确配置群集以允许在裂脑方案下运行)。

位置透明度
  • Orleans Grains不知道其物理位置,这也可以在系统运行时透明地更改。Grain函数的用户不需要知道他们正在与之交谈的Grain激活的位置。Grain引用也可以作为消息的一部分发送或保留。
  • 在Akka中,“位置透明性”的中心抽象是ActorRef:它是Actor位置的自包含可序列化表示形式,使其他Actor可以向其发送消息。ActorRef也可以作为消息的一部分发送,以相互介绍Actor。

自动横向扩展
  • 可以将Orleans Grains配置为无状态工作程序,该工作程序允许在任何给定时间激活多个激活。运行时响应于工作负载来管理缩放因子。
  • 在Akka中,支持群集的路由器提供与Orleans无状态工作模式相同的功能,不同之处在于,随着群集的增长,所提供的抽象为程序提供了可用的资源,但是Akka本身不包含增加额外节点的逻辑或能力以应对增加的负载。这留给了其他管理服务器舰队的工具。

异步性
  • 这两个库都实现了纯异步编程模型。

单线程
  • 这两个库都以N:M的方式将Actor映射到线程,其中N通常比M大得多。

合作多任务
  • 这两个库均允许Ac​​tor一次处理一条消息,而不会造成中断或时间限制。

序列化
  • Orleans 包含用于生成所有Grain调用的序列化和反序列化代码的代码生成器。
  • Akka的序列化子系统是完全可配置的,用户可以使用任何库在所需的对象和ByteString之间进行中介。

最终一致性
  • Orleans的分布式目录和托管服务最终是一致的,这意味着在集群拓扑更改期间(尤其是在故障之后),单个激活Grain的多个激活可能共存。
  • Akka群集取决于根据用户选择的策略或用户实施的策略来明确删除故障节点,该策略确定在网络分裂的情况下,群集是否有利于可用性(如奥尔良所做的那样)或一致性(这意味着群集在某些情况下会完全关闭)。当ClusterSharding为响应负载变化或故障而移动碎片时,某些逻辑Actor名称可能暂时不可用,并且其传入消息将被保留和/或重新路由(在限制范围内);如果将集群配置为支持一致性,则ClusterSharding将永远不会为单个逻辑Actor创建两个实例。

消息传递保证
  • Orleans保证最多使用一次语义,但是它不包含序列号以确保每对消息的排序,这意味着可以在不等待前一个的答复的情况下发出对Grain的调用,而等待下一个的发送该谷物的顺序。
  • Akka还为Actor消息的正常发送提供最多一次的保证(对于监督通知,为非持久性的一次),并保证每个发送者-接收者对之间的消息排序。

这两个系统都包含了对至少一次语义的选择加入支持,以及内在的警告,即接收者可能会收到重复的消息-它们都不包含重复数据删除。

banq注:以上是Akka自己发布的对比,更多点击标题见原文。这种对比忽视了一个最能吸引用户的概念:Orleans类似EJB自动地支持分布式事务,当然这种组件级别分布式事务也存在误用,也受到CAP定理的约束,关键取决于你对业务划分的粒度,如果粒度过大,分布式事务中聚合了太多业务交易,那么可能无法平滑伸缩。吞吐量下降;同样,如果粒度过细,延迟会增加。业务粒度的划分可参考DDD有界上下文。
Akka的分布式事务是通过DDD+EventSourcing概念实现,这个过程需要用户介入的要更多些,但是也更具有灵活性,不会把分布式事务当成银弹,其实它是双刃剑,如果很好用就会不限度使用,而事务的事务必须有严格的业务分析,这也是Transaction翻译成事务和交易两个中文意思,事务是技术组件面提供通用功能,而且受到CAP定理限制和误用惩罚,使用技术组件或数据库的分布式事务机制,一开始很爽很快,但是当你将很多交易流程揉入一条分布式事务,这个事务不但执行长,吞吐量并发无法提升上去,到时再切分非常痛苦,等同于补课,重新进行业务粒度划分。
分布式事务可能是个伪概念