Erlang/OTP消息是否可靠?邮件可以重复吗?

jos*_*hng 36 erlang messaging fault-tolerance transactions reliability

长版:

我是erlang的新手,并考虑将其用于可扩展的架构.我发现该平台的许多支持者都在宣传其可靠性和容错性.

但是,我很难准确理解在这个消息在瞬态内存中排队的系统中如何实现容错.我知道可以安排一个主管层级来重新生成已故的流程,但我一直无法找到很多关于重生工作对正在进行的工作的影响的讨论.在飞行中的消息和在垂死节点上丢失的部分完成的工作的工件会发生什么?

当消费者进程死亡时,所有生成器是否会自动重新传输未被确认的消息?如果没有,这怎么可以被认为是容错的?如果是这样的话,是什么阻止了被处理的消息 - 但不是很确认 - 被重新传输,因此不适当地重新处理?

(我认识到这些问题不是erlang独有的;在任何分布式处理系统中都会出现类似的问题.但是erlang爱好者似乎声称平台让这一切变得"简单"......?)

假设重新传输消息,我可以很容易地想象出一个复杂的消息链的下游影响在发生故障后可能变得非常混乱的情况.如果没有某种繁重的分布式事务系统,我不明白如何在不解决每个进程中的重复的情况下保持一致性和正确性.我的应用程序代码是否必须始终强制执行约束以防止事务被多次执行?

精简版:

分布式erlang进程是否受重复消息的影响?如果是这样,是重复保护(即,幂等)应用程序的责任,还是erlang/OTP以某种方式帮助我们?

I G*_*ICE 110

我将把它分成我希望有意义的要点.我可能会重新编写一下我在"Hitchhiker的并发指南"中所写的内容.您可能希望阅读该内容以获取有关在Erlang中完成消息传递方式背后的基本原理的详细信息.


1.消息传输

在Erlang中传递的消息是通过发送到邮箱的异步消息(一种用于存储数据的队列)完成的.绝对没有关于是否收到消息,甚至是否已将其发送到有效进程的假设.这是因为假设[在语言层面]有人可能只想在4天内处理一条消息并且在它达到某个状态之前甚至不会确认它的存在是合理的.

一个随机的例子可能是想象一个长时间运行的过程,将数据压缩4个小时.它真的应该承认它收到了一条消息,如果它无法治疗吗?也许它应该,也许不是.这真的取决于你的应用程序.因此,没有做出任何假设.您可以将一半的消息异步,只有一个不是.

如果您需要,Erlang希望您发送确认消息(并在超时时等待它).与超时有关的规则和回复的格式留给程序员指定 - 当任务完成时,Erlang不能假设你想要对消息接收的确认,是否匹配(消息当新版本的代码被热载时,可以在4小时内匹配)等.

简而言之,无论是否未读取消息,未能接收消息,或者在传输过程中拔出插头的人都会中断消息,如果您不想要它.如果您希望它重要,您需要跨流程设计逻辑.

Erlang进程之间实现高级消息协议的负担是给程序员的.


2.消息协议

正如您所说,这些消息存储在瞬态存储器中:如果进程终止,则尚未读取的所有消息都将丢失.如果你想要更多,有各种策略.其中一些是:

  • 尽可能快地读取消息并根据需要将其写入磁盘,发送确认并稍后处理.将此与RabbitMQ和ActiveMQ等队列软件与持久队列进行比较.
  • 使用进程组在多个节点上的一组进程中复制消息.此时,您可以输入事务语义.这个用于事务提交的mnesia数据库;
  • 在收到确认一切正常或失败消息的确认之前,不要假设任何工作有效
  • 进程组和失败消息的组合.如果第一个进程无法处理任务(因为节点发生故障),则VM会自动将通知发送到故障转移进程,而该进程会处理它.此方法有时与完整应用程序一起使用以处理硬件故障.

根据手头的任务,您可以使用其中一个或多个.它们都可以在Erlang中实现,并且在很多情况下,模块已经编写完毕,可以为您完成繁重的任务.

So this might answer your question. Because you implement the protocols yourself, it's your choice whether messages get sent more than once or not.


3. What is fault-tolerance

Picking one of the above strategies does depend on what fault-tolerance means to you. In some cases, people mean it to say "no data is ever lost, no task ever fails." Other people use fault-tolerance to say "the user never sees a crash." In the case of Erlang systems, the usual meaning is about keeping the system running: it's alright to maybe have a single user dropping a phone call rather than having everyone dropping it.

Here the idea is then to let stuff that fails fail, but keep the rest running. To achieve this, there are a few things the VM gives you:

  • You can know when a process dies and why it did
  • You can force processes that depend on each other to die together if one of them goes wrong
  • You can run a logger that automatically logs every uncaught exception for you, and even define your own
  • Nodes can be monitored so you know when they went down (or got disconnected)
  • You can restart failed processes (or groups of failed processes)
  • Have whole applications restarting on different nodes if one fails
  • And a lot more more stuff with the OTP framework

With these tools and a few of the standard library's modules handling different scenarios for you, you can implement pretty much what you want on top of Erlang's asynchronous semantics, although it usually pays to be able to use Erlang's definition of fault tolerance.


4. A few notes

我个人的观点是,除非你想要纯粹的事务语义,否则比Erlang中存在更多的假设是非常困难的.一直遇到问题的一个问题是节点发生故障.您永远无法知道它们是否因为服务器实际崩溃或网络故障而关闭.

在服务器崩溃的情况下,只需重新执行任务就足够了.但是,如果进行净拆分,您必须确保一些重要操作不会进行两次,但也不会丢失.

它通常归结为CAP定理,基本上给你3个选项,你必须选择两个:

  1. 一致性
  2. 分区容差
  3. 可用性

根据您自己的位置,将需要不同的方法.CAP定理通常用于描述数据库,但我相信在处理数据时需要一定程度的容错能力时会询问类似的问题.

  • 我们需要一种方法来捐赠我们的一些积分,以便将答案提高+1以上.这里认真工作. (5认同)

Jer*_*all 5

erlang OTP系统具有容错能力.这并不能减轻您在其中构建同样容错的应用程序的需要.如果您使用erlang和OTP,那么您可以依赖一些东西.

  1. 当进程终止时,该进程将重新启动.
  2. 在大多数情况下,进程崩溃不会导致整个应用程序崩溃
  3. 当发送消息时,如果接收器存在,将接收消息.

据我所知,erlang中的消息不会重复.如果您发送消息并且进程收到消息,则消息将从队列中消失.但是,如果您发送消息并且进程收到该消息但在处理它时崩溃,则该消息将消失并且未处理.在您的系统设计中应该考虑这个事实.OTP通过使用进程从可能遭受崩溃的应用程序代码中隔离基础架构关键代码(例如,supervisor,gen_servers,...)来帮助您处理所有这些.

例如,您可能有一个gen_server将工作分派给进程池.池中的进程可能会崩溃并重新启动.但gen_server仍然存在,因为它的全部目的只是接收消息并将它们发送到池中进行处理.这样可以让整个系统在游泳池中出现错误和崩溃的情况下保持稳定,并且始终有一些东西在等待您的消息.

仅仅因为系统是容错的并不意味着你的算法是.