原文:https://doc.akka.io/docs/akka/2.5/general/message-delivery-reliability.htmlhtml
Akka可帮助您构建可靠的应用程序,这些应用程序在一台计算机中使用多个处理器核心或分布在计算机网络中。使这项工做的关键抽象是你的代码单元 -Actors之间的全部交互都是经过消息传递发生的,这就是为何消息如何在actor之间传递的精确语义值得讲的章节。安全
为了给下面的讨论提供一些上下文,请考虑跨越多个网络主机的应用程序。不管是发送到本地JVM上的actor仍是远程actor,通讯的基本机制都是相同的,可是在传递延迟方面会有明显的差别(可能还取决于网络链路的带宽和消息大小)和可靠性。若是发送远程消息,则涉及更多步骤,这意味着更多可能出错。另外一个方面是本地发送将传递对同一JVM内的消息的引用,而不对发送的基础对象进行任何限制,而远程传输将对消息大小设置限制。性能优化
编写你的Actor使得每次互动均可能是遥远的是安全的。这意味着只依赖于那些始终保证的属性,这些属性将在下面详细讨论。这在actor的实现中有一些开销。若是您愿意牺牲完整的位置透明度 - 例如在一组密切合做的Actors的状况下 - 您能够将它们始终放在同一个JVM上,并在邮件传递上享受更严格的保证。下面将进一步讨论这种权衡的细节。网络
做为补充部分,咱们提供了一些指导,说明如何在内置的可靠性之上构建更强的可靠性。本章最后讨论了“Dead Letter Office”的做用。分布式
这些是消息发送的规则(即tell或!方法,它也是ask模式的基础):工具
第一个规则一般也能够在其余actor实现中找到,而第二个规则特定于Akka。性能
在描述传递机制的语义时,有三个基本类别:测试
at-most-once:传递意味着对于传递给机制的每条消息,该消息一次传递或根本不传递;从更随意的角度来看,这意味着消息可能会丢失。优化
at-least-once:传递意味着对于传递给该机制的每一个消息,可能在传递它时进行屡次尝试,使得至少一个成功;再次,从更随意的角度来看,这意味着消息可能会重复但不会丢失。spa
exactly-once:交付意味着对于递交给机制的每一个消息,只有一个交付给接收者;消息既不会丢失也不会重复。
第一个是最便宜的最高性能,最少的实现开销 - 由于它能够以一种即发即忘的方式完成,而不会在发送端或传输机制中保持状态。第二个须要重试以对抗传输损耗,这意味着将状态保持在发送端而且在接收端具备确认机制。第三个是最昂贵的 - 而且所以具备最差的性能 - 由于除了第二个以外它还须要将状态保持在接收端以便过滤掉重复的交付。
问题的核心在于这个保证究竟意味着什么:
其中每个都有不一样的挑战和成本,很明显,在任何状况下,任何消息传递库都没法遵照。
沿着一样的路线,无人须要可靠消息的推理。发送者知道交互是否成功的惟一有意义的方式是接收业务级别的确认消息,这不是Akka能够本身构成的。
Akka采用分布式计算,并经过消息传递使通讯的可靠性显而易见,所以它不会试图欺骗和模拟漏洞抽象。这是一个在Erlang中取得巨大成功的模型,须要用户围绕它设计应用程序。您能够在Erlang文档(第10.9节和第10.10节)中阅读有关此方法的更多信息,Akka会密切关注它。
在这个问题上的另外一个角度是,经过仅提供基本保证,那些不须要更高可靠性的用例不支付其实施成本;老是能够在基本的基础上增长更强的可靠性,可是为了得到更高的性能,不可能主动地去除可靠性。
规则更具体地说,对于给定的一对Actor,直接从第一个发送到第二个的消息不会无序接收。这个词直接强调,这种保证仅适用于与告诉操做员一块儿发送到最终目的地,而不是在使用调解员或其余信息传播功能时(除非另有说明)。
他保证说明以下:
Actor A1
sends messages M1
, M2
, M3
to A2
Actor A3
sends messages M4
, M5
, M6
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 M
, F
or F
, M
缘由是内部系统消息具备本身的邮箱,所以用户和系统消息的排队调用的排序不能保证其出队时间的排序。
建议不要依赖本节中更强的可靠性,由于它会将您的应用程序绑定到仅本地部署:应用程序可能必须以不一样的方式设计(而不是仅仅采用某些参与者本地的一些消息交换模式)才能适合在一组机器上运行。咱们的信条是“设计一次,按照您但愿的方式进行部署”,为实现这一目标,您应该只依赖于“通常规则”。
Akka测试套件依赖于不丢失本地上下文中的消息(以及用于远程部署的非错误条件测试),这意味着咱们确实尽最大努力保持测试稳定。可是,本地tell操做可能会失败的缘由与JVM上的普通方法调用相同:
StackOverflowError
OutOfMemoryError
VirtualMachineError
此外,本地发送可能会以特定于Akka的方式失败:
虽然第一个是配置问题,但第二个值得一些考虑:若是在处理时出现异常,则消息的发送者不会获得反馈,而是通知发送给主管。这一般没法与外部观察者丢失的信息区分开来。
假设严格的FIFO邮箱,在某些条件下消除了上述消息排序保证的非传递性警告。正如您将注意到的,这些都是很是微妙的,将来的性能优化甚至可能使整个段落无效。
此列表已通过仔细编译,但其余有问题的状况可能已经逃脱了咱们的分析。
基于Akka核心的小而一致的工具集,Akka还提供了强大的,更高级别的抽象。
如上所述,对可靠传送要求的直接回答是明确的ACK-RETRY协议。这是最简单的形式;
没法传递的消息(而且能够肯定这些消息)将被传递给名为/ 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