Akka消息传送可靠性 23

原文:https://doc.akka.io/docs/akka/2.5/general/message-delivery-reliability.html

Akka可帮助您构建可靠的应用程序,这些应用程序在一台计算机中使用多个处理器核心或分布在计算机网络中。使这项工作的关键抽象是你的代码单元 -Actors之间的所有交互都是通过消息传递发生的,这就是为什么消息如何在actor之间传递的精确语义值得讲的章节。

为了给下面的讨论提供一些上下文,请考虑跨越多个网络主机的应用程序。无论是发送到本地JVM上的actor还是远程actor,通信的基本机制都是相同的,但是在传递延迟方面会有明显的差异(可能还取决于网络链路的带宽和消息大小)和可靠性。如果发送远程消息,则涉及更多步骤,这意味着更多可能出错。另一个方面是本地发送将传递对同一JVM内的消息的引用,而不对发送的基础对象进行任何限制,而远程传输将对消息大小设置限制。

编写你的Actor使得每次互动都可能是遥远的是安全的。这意味着只依赖于那些始终保证的属性,这些属性将在下面详细讨论。这在actor的实现中有一些开销。如果您愿意牺牲完整的位置透明度 - 例如在一组密切合作的Actors的情况下 - 您可以将它们始终放在同一个JVM上,并在邮件传递上享受更严格的保证。下面将进一步讨论这种权衡的细节。

作为补充部分,我们提供了一些指导,说明如何在内置的可靠性之上构建更强的可靠性。本章最后讨论了“Dead Letter Office”的作用。

一般规则

这些是消息发送的规则(即tell或!方法,它也是ask模式的基础):

  • at-most-once delivery
  • message ordering per sender–receiver pair

第一个规则通常也可以在其他actor实现中找到,而第二个规则特定于Akka。

讨论:“at-most-once”是什么意思?

在描述传递机制的语义时,有三个基本类别:

  • at-most-once:传递意味着对于传递给机制的每条消息,该消息一次传递或根本不传递;从更随意的角度来看,这意味着消息可能会丢失。

  • at-least-once:传递意味着对于传递给该机制的每个消息,可能在传递它时进行多次尝试,使得至少一个成功;再次,从更随意的角度来看,这意味着消息可能会重复但不会丢失。

  • exactly-once:交付意味着对于递交给机制的每个消息,只有一个交付给接收者;消息既不会丢失也不会重复。

第一个是最便宜的最高性能,最少的实现开销 - 因为它可以以一种即发即忘的方式完成,而不会在发送端或传输机制中保持状态。第二个需要重试以对抗传输损耗,这意味着将状态保持在发送端并且在接收端具有确认机制。第三个是最昂贵的 - 并且因此具有最差的性能 - 因为除了第二个之外它还需要将状态保持在接收端以便过滤掉重复的交付。

讨论:为什么没有保证传递?

问题的核心在于这个保证究竟意味着什么:

  1. 消息是在网络上发出的?
  2. 该消息是由其他主机收到的?
  3. 消息被放入目标actor的邮箱?
  4. 消息开始由目标actor处理?
  5. 目标actor是否成功处理了该消息?

其中每一个都有不同的挑战和成本,很明显,在任何情况下,任何消息传递库都无法遵守。

沿着同样的路线,无人需要可靠消息的推理。发送者知道交互是否成功的唯一有意义的方式是接收业务级别的确认消息,这不是Akka可以自己构成的。

Akka采用分布式计算,并通过消息传递使通信的可靠性显而易见,因此它不会试图欺骗和模拟漏洞抽象。这是一个在Erlang中取得巨大成功的模型,需要用户围绕它设计应用程序。您可以在Erlang文档(第10.9节和第10.10节)中阅读有关此方法的更多信息,Akka会密切关注它。

在这个问题上的另一个角度是,通过仅提供基本保证,那些不需要更高可靠性的用例不支付其实施成本;总是可以在基本的基础上增加更强的可靠性,但是为了获得更高的性能,不可能主动地去除可靠性。

讨论:消息排序

规则更具体地说,对于给定的一对Actor,直接从第一个发送到第二个的消息不会无序接收。这个词直接强调,这种保证仅适用于与告诉操作员一起发送到最终目的地,而不是在使用调解员或其他信息传播功能时(除非另有说明)。

他保证说明如下:

Actor A1 sends messages M1M2M3 to A2

Actor A3 sends messages M4M5M6 to A2

意味着:

1.如果M1已交付,则必须在M2和M3之前交付

2.如果M2已交付,则必须在M3之前交付

3.如果交付M4,则必须在M5和M6之前交付

4.如果M5已交付,则必须在M6之前交付

5.A2可以看到来自A1的消息与来自A3的消息交织

6.由于没有保证传送,因此可以丢弃任何消息,即不到达A2

请务必注意,Akka的保证适用于邮件排入收件人邮箱的顺序。如果邮箱实现不遵守FIFO顺序(例如PriorityMailbox),那么actor的处理顺序可能会偏离排队顺序。

请注意,此规则不具有传递性:

Actor A sends message M1 to actor C

Actor A then sends message M2 to actor B

Actor B forwards message M2 to actor C

Actor C may receive M1 and M2 in any order

因果传递排序意味着在Actor C的M1之前从未接收过M2(尽管其中任何一个都可能丢失)。当A,B和C驻留在不同的网络主机上时,由于不同的消息传递延迟,可能会违反此排序,请参阅下文。

沟通失败

请注意,上面讨论的排序保证仅适用于Actor之间的用户消息。actor的孩子的失败是通过相对于普通用户消息未被排序的特殊系统消息来传达的。特别是:

Child actor C sends message M to its parent P

Child actor fails with failure F

Parent actor P might receive the two events either in order MF or F, M

原因是内部系统消息具有自己的邮箱,因此用户和系统消息的排队调用的排序不能保证其出队时间的排序。

In-JVM(本地)消息发送规则

建议不要依赖本节中更强的可靠性,因为它会将您的应用程序绑定到仅本地部署:应用程序可能必须以不同的方式设计(而不是仅仅采用某些参与者本地的一些消息交换模式)才能适合在一组机器上运行。我们的信条是“设计一次,按照您希望的方式进行部署”,为实现这一目标,您应该只依赖于“一般规则”。

Akka测试套件依赖于不丢失本地上下文中的消息(以及用于远程部署的非错误条件测试),这意味着我们确实尽最大努力保持测试稳定。但是,本地tell操作可能会失败的原因与JVM上的普通方法调用相同:

  • StackOverflowError
  • OutOfMemoryError
  • other VirtualMachineError

此外,本地发送可能会以特定于Akka的方式失败:

  • 如果邮箱不接受该邮件(例如完整的BoundedMailbox)
  • 如果接收方在处理消息时失败或已经终止

虽然第一个是配置问题,但第二个值得一些考虑:如果在处理时出现异常,则消息的发送者不会得到反馈,而是通知发送给主管。这通常无法与外部观察者丢失的信息区分开来。

本地消息发送的排序

假设严格的FIFO邮箱,在某些条件下消除了上述消息排序保证的非传递性警告。正如您将注意到的,这些都是非常微妙的,未来的性能优化甚至可能使整个段落无效。

  • 在收到来自顶级actor的第一个回复之前,有一个锁保护内部临时队列,这个锁是不公平的;这意味着可以根据低级线程调度重新排序来自在演员构造期间到达的不同发送者的入队请求(比喻地,细节更多涉及)。由于JVM上不存在完全公平的锁,因此这是不可修复的。
  • 在构建路由器期间使用相同的机制,更确切地说是路由的ActorRef,因此使用路由器部署的actor存在同样的问题。
  • 如上所述,问题发生在排队期间涉及锁定的任何地方,这也可能适用于自定义邮箱。

此列表已经过仔细编译,但其他有问题的情况可能已经逃脱了我们的分析。

更高层次的抽象

 基于Akka核心的小而一致的工具集,Akka还提供了强大的,更高级别的抽象。

如上所述,对可靠传送要求的直接回答是明确的ACK-RETRY协议。这是最简单的形式;

  • 一种识别单个消息以将消息与确认相关联的方法
  • 重试机制,如果未及时确认,将重新发送消息
  • 接收器检测和丢弃重复的方法

Dead Letters

无法传递的消息(并且可以确定这些消息)将被传递给名为/ deadLetters的合成actor。这种交付是在尽力而为的基础上进行的;它甚至可能在本地JVM内失败(例如在Actor终止期间)。通过不可靠的网络传输发送的消息将丢失而不会显示为dead letter。

此工具的主要用途是用于调试,特别是如果actor发送不一致(通常检查死信会告诉您发送者或收件人在途中某处设置错误)。为了有用于此目的,最好避免在可能的情况下发送到deadLetters,即不时使用合适的死信记录器运行应用程序(请参阅下面的详细信息)并清理日志输出。这个练习就像所有其他一样 - 需要明智地应用常识:很可能避免发送给已终止的演员使发送者的代码比调试输出清晰度更复杂。

死信服务遵循与所有其他消息发送相同的交付保证规则,因此不能用于实现有保证的交付。

我如何收到Dead Letter?

actor可以在事件流上订阅类akka.actor.DeadLetter,请参阅事件流以了解如何执行此操作。然后,订阅的演员将从该点开始接收在(本地)系统中发布的所有DeadLetter。DeadLetter不会通过网络传播,如果要在一个地方收集死信,则必须为每个网络节点订阅一个参与者并手动转发它们。还要考虑在该节点生成DeadLetter,这可以确定发送操作失败,远程发送可以是本地系统(如果不能建立网络连接)或远程发送(如果发送的是Actor)在那个时间点不存在)。

DeadLetter(通常)并不令人担忧

每当一个Actor按照自己的决定终止时,它发送给自己的某些消息就有可能丢失。有一个在复杂的关闭场景中很容易发生,通常是良性的:看到akka.dispatch.Terminate消息被丢弃意味着给出了两个终止请求,但只有一个可以成功。同样地,你可能会看到akka.actor.Terminated来自子节点的消息,同时如果父节点在终止时仍在观看子节点。

未完待续!

原文:https://doc.akka.io/docs/akka/2.5/general/message-delivery-reliability.html

转载于:https://my.oschina.net/u/2277632/blog/3003415

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值