您如何设计基于Erlang / OTP的分布式容错多核系统的架构?

问题描述 投票:40回答:1

我想构建一个基于Erlang / OTP的系统,它解决了一个“令人尴尬的并行”问题。

我已阅读/浏览过:

  • 了解一些Erlang;
  • Erlang编程(阿姆斯特朗);
  • Erlang编程(Cesarini);
  • Erlang / OTP在行动中。

我有进程,消息,监督,gen_servers,Logging等的要点。

我确实理解某些架构选择取决于所关注的应用程序,但我仍然想知道ERlang / OTP系统设计的一些一般原则。

我应该从一个主管的几个gen_servers开始,并逐步建立?

我应该有多少名主管?如何确定系统的哪些部分应基于流程?我该如何避免瓶颈?

我以后应该添加日志吗?

Erlang / OTP分布式容错多处理器系统架构的一般方法是什么?

architecture erlang otp
1个回答
104
投票

我应该从一个主管的几个gen_servers开始,并逐步建立?

你在这里错过了Erlang架构中的一个关键组件:应用程序! (也就是说,OTP应用程序的概念,而不是软件应用程序)。

将应用程序视为组件。系统中的一个组件解决了一个特定的问题,负责一组连贯的资源或从系统中抽象一些重要或复杂的东西。

设计Erlang系统的第一步是确定需要哪些应用程序。有些可以按原样从网络中提取,我们可以将其称为库。您需要自己编写的其他人(否则您不需要这个特定的系统)。我们通常将这些应用程序称为业务逻辑(通常您也需要自己编写一些库,但保持库与将所有内容绑定在一起的核心业务应用程序之间的区别很有用)。

我应该有多少名主管?

您应该为要监控的每种流程都配备一名主管。

一堆相同的临时工?一位主管统治他们。

不同的流程有不同的职责和重启策略?每个不同类型的流程的主管,处于正确的层次结构中(取决于什么时候应该重新启动以及其他流程需要与它们一起下去?)。

有时可以在同一个主管下放置一堆不同的流程类型。当您有一些将始终运行的单个进程(例如,一个HTTP服务器管理程序,一个ETS表所有者进程,一个统计信息收集器)时,通常会出现这种情况。在这种情况下,每个人都有一个主管可能太过残忍,所以通常会在一个主管下面添加一个主管。请注意在执行此操作时使用特定重新启动策略的含义,因此您不要删除统计信息过程,例如,万一您的Web服务器崩溃(one_for_one是在这种情况下使用的最常见策略)。注意不要在one_for_one主管的进程之间有任何依赖关系。如果一个进程依赖于另一个崩溃的进程,它也会崩溃,过于频繁地触发主管的重启强度,并且过早地使主管本身崩溃。这可以通过具有两个不同的监督器来避免,这些监督者将通过配置的强度和周期(longer explanation)完全控制重启。

如何确定系统的哪些部分应基于流程?

系统中的每个并发活动都应该在它自己的进程中。错误的并发抽象是Erlang系统设计人员最常犯的错误。

有些人不习惯处理并发问题;他们的系统往往太少了。一个过程,或几个巨大的过程,按顺序运行一切。这些系统通常充满代码气味,代码非常严格,难以重构。它也使它们变慢,因为它们可能不会使用Erlang可用的所有核心。

其他人立即掌握并发概念,但未能以最佳方式应用它们;他们的系统倾向于过度使用流程概念,使许多流程闲置等待正在工作的其他人。这些系统往往不必要地复杂且难以调试。

从本质上讲,在两种变体中都会遇到同样的问题,您不会使用所有可用的并发性,并且您无法获得系统的最大性能。

如果您坚持使用single responsibility principle并遵守规则,为系统中的每个真正并发活动创建一个进程,那么您应该没问题。

有正当理由有闲置进程。有时他们会保持重要的状态,有时你想暂时保留一些数据,然后放弃这个过程,有时他们会等待外部事件。更大的缺陷是通过长链非常不活跃的进程传递重要消息,因为它会通过大量复制减慢系统速度并使用更多内存。

我该如何避免瓶颈?

很难说,很大程度上取决于你的系统以及它正在做什么。但是,一般来说,如果您在应用程序之间有一个良好的责任分工,那么您应该能够将与该系统其他部分分开的应用程序扩展为瓶颈。

这里的黄金法则是衡量,衡量,衡量!在你测量之前,不要认为你有什么需要改进的地方。

Erlang的优点在于它允许您隐藏接口后的并发(称为隐式并发)。例如,您使用功能模块API,一个普通的module:function(Arguments)接口,它可以反过来生成数千个进程,而无需调用者知道。如果您的抽象和API正确,您可以在开始使用它之后始终并行化或优化库。

话虽如此,这里有一些一般性的指导方针:

  • 尝试直接向收件人发送邮件,避免通过中间进程引导或路由邮件。否则系统会花费时间移动消息(数据)而不会真正起作用。
  • 不要过度使用OTP设计模式,例如gen_servers。在许多情况下,您只需要启动一个进程,运行一些代码,然后退出。为此,gen_server是矫枉过正的。

还有一个好处是:不要重复使用流程。在Erlang中生成一个进程是如此便宜和快速,一旦它的生命周期结束,重用一个进程是没有意义的。在某些情况下,重新使用状态(例如,文件的复杂解析)可能是有意义的,但是更好地规范地存储在其他地方(在ETS表,数据库等中)。

我以后应该添加日志吗?

您应该立即添加日志记录!有一个很棒的内置API,名为Logger,它带有版本21的Erlang / OTP:

logger:error("The file does not exist: ~ts",[Filename]),
logger:notice("Something strange happened!"),
logger:debug(#{got => connection_request, id => Id, state => State},
             #{report_cb => fun(R) -> {"~p",[R]} end}),

这个新API有几个高级功能,应该涵盖大多数需要记录的情况。还有较旧但仍广泛使用的第三方库Lager

Erlang / OTP分布式容错多处理器系统架构的一般方法是什么?

总结一下上面说的:

  • 将您的系统划分为应用程序
  • 根据需求和依赖关系,将您的流程置于正确的监督层次结构中
  • 为系统中的每个真正并发活动创建一个流程
  • 维护系统中其他组件的功能API。这可以让你: 重构代码而不更改使用它的代码 之后优化代码 在需要时分发您的系统(只需调用API后面的另一个节点!调用者不会注意到!) 更轻松地测试代码(减少设置测试工具的工作量,更容易理解如何使用它)
  • 开始使用OTP中可用的库,直到你需要不同的东西(你知道,到时候)

常见的陷阱:

  • 过程太多了
  • 流程太少了
  • 路由太多(转发的消息,链接的进程)
  • 应用程序太少(实际上我从未见过相反的情况)
  • 没有足够的抽象(很难重构和推理。它也很难测试!)
© www.soinside.com 2019 - 2024. All rights reserved.