您如何devise基于Erlang / OTP的分布式容错多核系统的体系结构?

我想build立一个基于Erlang / OTP的系统来解决一个“不平行的问题”。

我已经阅读/浏览过:

  • 学习一些Erlang;
  • 编程Erlang(Armstrong);
  • Erlang编程(Cesarini);
  • Erlang / OTP在行动。

我掌握了进程,消息传递,主pipe,gen_servers,日志logging等的要点。

我明白,某些架构select取决于所关注的应用程序,但我仍然想知道一些一般的ERlang / OTP系统devise原则。

我应该从一个主pipe人员开始,并逐渐build立在这个基础上?

我应该有多less名主pipe? 我如何确定系统的哪一部分应该是基于stream程的? 我应该如何避免瓶颈?

我应该稍后添加日志?

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

我应该从一个主pipe人员开始,并逐渐build立在这个基础上?

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

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

deviseErlang系统的第一步是决定需要哪些应用程序。 有些可以从网上拉出来,我们可以把它们称为图书馆。 其他你需要写自己(否则你不会需要这个特定的系统)。 这些应用程序通常被称为业务逻辑(通常你也需要自己编写一些库,但是保持库和核心业务应用程序之间的区别是非常有用的)。

我应该有多less名主pipe?

你应该有一个监督你想要监视的每种过程。

一堆相同的临时工? 一个主pipe统治他们。

不同的过程与不同的责任和重启战略? 每个不同types的过程的主pipe,在一个正确的层次结构中(取决于什么时候应该重新启动,什么其他过程需要与他们一起下去?)。

有时可以在同一个pipe理者下面放置一堆不同的stream程types。 通常情况下,当您有几个将始终运行的单例进程(例如,一个HTTP服务器监控程序,一个ETS表所有者进程,一个统计信息收集器)时, 在这种情况下,每个pipe理人员可能过多,所以通常会增加一个pipe理人员。 只需要知道在执行此操作时使用特定的重新启动策略的含义,所以不要将统计过程取下来,例如万一您的Web服务器崩溃( one_for_one是在这种情况下最常用的策略)。 要小心不要在one_for_one主pipe中的进程之间有任何依赖关系。 如果一个进程依赖于另一个崩溃的进程,它也可能崩溃,经常触发主pipe的重启强度,导致主pipe本身过早崩溃。 这可以通过使用两个不同的监督者来避免,这将通过configuration的强度和时期来完全控制重启( 更长的解释 )。

我如何确定系统的哪一部分应该是基于stream程的?

系统中的每个并行活动都应该在自己的过程中。 错误的并发抽象是Erlang系统devise者一开始最常犯的错误。

有些人不习惯处理并发问题; 他们的系统往往太less了。 一个过程,或几个巨大的过程,依次运行一切。 这些系统通常充满代码气味,代码非常僵硬,难以重构。 这也使得它们变慢,因为它们可能不会使用Erlang提供的所有内核。

其他人立即掌握并发概念,但是没有把它们应用到最优; 他们的系统倾向于过度使用过程概念,使许多进程闲置在等待正在工作的其他人。 这些系统往往是不必要的复杂,难以debugging。

实质上,在这两种变体中,您都会遇到同样的问题,您不会使用所有可用的并发性,也不会获得最大的性能。

如果你坚持单一职责原则,并遵守规则,为你的系统中的每个真正的并行活动build立一个过程,你应该没问题。

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

我应该如何避免瓶颈?

很难说,很大程度上取决于你的系统和它在做什么。 一般来说,如果你在应用程序之间有一个很好的责任分工,那么你应该能够将看起来像是瓶颈的应用程序与系统的其他部分分开。

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

Erlang的优点在于它允许隐藏接口后面的并发(称为隐式并发)。 例如,你使用一个function模块API,一个正常的module:function(Arguments)接口,可以反过来派生成千上万的进程,调用者不必知道这一点。 如果你的抽象和你的API是正确的,你可以在开始使用它之后总是对库进行并行或优化。

这就是说,这里有一些一般的指导方针:

  • 尝试直接向收件人发送消息,避免通过中间进程传送或路由消息。 否则,系统只是花费时间移动消息(数据)而没有真正的工作。
  • 不要过度使用OTPdevise模式,比如gen_servers。 在很多情况下,你只需要启动一个进程,运行一段代码,然后退出。 为此,gen_server是矫枉过正的。

还有一个奖励build议:不要重复使用stream程。 在Erlang中产生一个进程是如此便宜和快速,以至于一旦生命周期结束,重用一个进程是没有意义的。 在某些情况下,重新使用状态可能是有意义的(例如对文件进行复杂的parsing),但是在其他地方(在ETS表,数据库等中)可以更好地存储。

我应该稍后添加日志?

Erlang / OTP中已经有一些基本的日志function, 错误logging器 。 与SASL (系统架构支持库)一起,您可以随时login并运行。

当时间到了(如果你从一开始就抽象了日志API),你可以交换一些更适合你需求的东西。 现在的事实上的第三方日志库是Basho的Lager 。

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

总结一下上面说过的话:

  • 将您的系统分成应用程序
  • 把你的stream程放在正确的监督层次上,取决于他们的需求和依赖关系
  • 为您的系统中的每个真正的并发活动build立一个stream程
  • 维护系统中其他组件的functionAPI。 这让你:
    • 重构您的代码,而不更改使用它的代码
    • 之后优化代码
    • 在需要的时候分配你的系统(只需要调用API之后的另一个节点!调用者不会注意到!)
    • testing代码更容易(less设置testing线束,更容易理解如何使用它)
  • 开始在OTP中使用可用的库,直到你需要不同的东西(到时候你就会知道)

常见的陷阱:

  • 太多的进程
  • 太less的过程
  • 路由太多(转发的消息,链接进程)
  • 太less的应用程序(我从来没有见过相反的情况,实际上)
  • 没有足够的抽象(难以重构和推理,这也使得很难testing!)