语言和可扩展性之间的关系

Posted

技术标签:

【中文标题】语言和可扩展性之间的关系【英文标题】:Relation between language and scalability 【发布时间】:2010-12-22 08:06:00 【问题描述】:

我在 Erlang 社区网站 Trapexit 中看到以下声明:

Erlang 是一种使用的编程语言 构建可大规模扩展的软件 具有以下要求的实时系统 高可用性。

我还记得在某处看到 Twitter 从 Ruby 切换到 Scala 以解决可扩展性问题。

因此,我想知道编程语言和可扩展性之间有什么关系?

我认为可扩展性仅取决于系统设计、异常处理等。是因为语言的实现方式、库还是其他一些原因?

希望开悟。谢谢。

【问题讨论】:

相关***.com/questions/1636455/… 【参考方案1】:

Erlang 针对电信环境进行了高度优化,运行时间大约为 5 个 9 秒。

它包含一组称为 OTP 的库,可以在不关闭应用程序的情况下“即时”将代码重新加载到应用程序中!此外,还有一个supervisor模块等框架,这样当某事发生故障时,它会自动重新启动,否则故障可以逐渐向上链,直到它到达一个可以处理它的supervisor模块。

当然,这在其他语言中也是可能的。在 C++ 中,您可以即时重新加载 dll,加载插件。在 Python 中,您可以重新加载模块。在 C# 中,您可以动态加载代码、使用反射等。

只是 Erlang 内置了该功能,这意味着:

它更标准,任何 erlang 开发人员都知道它是如何工作的 重新实现自己的东西更少

也就是说,语言之间存在一些根本差异,在某种程度上,有些是解释的,有些是字节码的,有些是本机编译的,所以在运行时的性能和类型信息的可用性等是不同的。

Python 在其运行时库周围有一个全局解释器锁,因此无法使用 SMP。

Erlang 只是最近才添加了更改以利用 SMP。

一般来说,我同意你的观点,我认为一个显着的区别在于内置库,而不是语言本身的根本区别。

最终,我觉得任何获得非常大的项目都有“陷入困境”的风险,无论它是用什么语言编写的。正如你所说,我觉得架构和设计对于可扩展性来说是非常基础的,我不会选择一种语言而不是另一种语言感觉神奇地提供了令人敬畏的可扩展性......

【讨论】:

感谢您提供相当详细的答案。我对 Erlang 太陌生,无法真正理解,但我明白了整个论点。谢谢 “五个九的正常运行时间”事实只是一个神话。它来自乔·阿姆斯特朗(Joe Armstrong)基于已经运行多年的系统以及在该窗口期间的停机时间所做的愚蠢计算。这就是现在计算可靠性的方式。如果是这样,那么任何连续运行 5 分钟的系统在该窗口内都具有 100% 的可靠性。 @Christian,傻不傻,“五个九”实际上是用(一年 - 停机时间)/一年来定义的。愚蠢的原因在于,对于 Erlang,人们更喜欢谈论“正常运行时间”的五个 9 而不是“服务可用性”的五个 9。如果消费者获得主管树的“五个九”,他可能不满意,而工作进程只存在“九个五”:)。 @Christian, Zed:在电信领域,Zed 是完全正确的。我确信网络运营商会被您的正常运行时间定义所欺骗。 我想说,语言的基本属性对于解释构建可扩展系统的差异非常重要。它们决定了您设计此类系统及其基本属性的基本方式。图书馆当然非常重要,但它们的结构和风格是由语言的属性决定的。所以,是的,您可以在 C++/Java 之上构建基于消息传递的基础设施,但不,它不会像在 Erlang 中那样安全,因为底层语言不会强制执行使其安全所需的内容。【参考方案2】:

首先,您必须区分语言及其实现。比如ruby语言支持线程,但在官方实现中,线程不会使用多核芯片。

然后,当一种语言/实现/算法支持并行计算(例如通过多线程)并且当 CPU 数量增加时它表现出良好的加速增加时,它通常被称为可扩展(请参阅阿姆达尔定律)。

ErlangScalaOz 等一些语言也有语法(或不错的库),可以帮助编写清晰和漂亮的并行代码.

【讨论】:

【参考方案3】:

Erlang 是一种在设计时考虑到并发性的语言。虽然大多数语言都依赖于操作系统进行多线程处理,但 Erlang 中内置了并发性。 Erlang 程序可以由数千到数百万个极其轻量级的进程组成,这些进程可以在单个处理器上运行,可以在多核处理器上运行,或者可以在处理器网络上运行。 Erlang 还对进程之间的消息传递、容错等提供了语言级别的支持。Erlang 的核心是一种函数式语言,函数式编程是构建并发系统的最佳范例。

简而言之,在 Erlang 中制作分布式、可靠和可扩展的系统很容易,因为它专门为此目的设计的语言。

【讨论】:

Erlang 是为可靠性而设计的,而不是专门为并发而设计的。并发是一个不错的奖励。【参考方案4】:

Erlang 来自另一种思考可靠性以及如何实现可靠性的文化。理解文化很重要,因为 Erlang 代码不会仅仅因为 Erlang 就神奇地变得容错。

一个基本的想法是,高正常运行时间不仅来自非常长的平均故障间隔时间,还来自非常短的平均恢复时间(如果发生故障)。

然后意识到当检测到故障时需要自动重新启动。并且人们意识到,在第一次检测到一些不太正确的事情时,应该“崩溃”以导致重新启动。恢复需要优化,可能的信息丢失需要最小化。

许多成功的软件都遵循此策略,例如日志文件系统或事务日志数据库。但绝大多数情况下,软件往往只考虑平均故障间隔时间并将有关错误指示的消息发送到系统日志,然后尝试继续运行,直到不再可能为止。通常需要人工监控系统并手动重启。

这些策略中的大多数都是 Erlang 中的库形式。作为语言特性的部分是进程可以相互“链接”和“监控”。第一个是双向合约,“如果你崩溃,我会收到你的崩溃消息,如果没有被困住,我会崩溃”,第二个是“如果你崩溃,我会收到有关它的消息”。

链接和监控是库用来确保其他进程(还没有)崩溃的机制。流程被组织成“监督”树。如果树中的一个工作进程失败,主管将尝试重新启动它,或者树中该分支的同一级别的所有工作人员。如果失败,它将升级,等等。如果***主管放弃应用程序崩溃并且虚拟机退出,此时系统操作员应该重新启动计算机。

进程堆之间的完全隔离是 Erlang 表现出色的另一个原因。除了少数例外,不可能在进程之间“共享值”。这意味着所有进程都是非常独立的,并且通常不会受到另一个进程崩溃的影响。此属性也适用于 Erlang 集群中的节点之间,因此处理节点故障退出集群的风险很低。复制和发送更改事件,而不是单点故障。

Erlang 采用的哲学有很多名称,“fail fast”、“crash-only system”、“面向恢复的编程”、“expose errors”、“micro-restarts”、“replication”...

【讨论】:

【参考方案5】:

Twitter 将其架构的某些部分从 Ruby 切换到 Scala,因为当他们开始工作时使用了错误的工具。他们正在使用 Ruby on Rails(针对构建新的 CRUD Web 应用程序进行了高度优化)来尝试构建消息传递系统。 AFAIK,他们仍在将 Rails 用于 Twitter 的 CRUD 部分,例如创建一个新的用户帐户,但已将消息传递组件移至更合适的技术。

【讨论】:

这并不能回答问题的基本方面,是吗? 我在 Twitter 示例中添加了更多细节。【参考方案6】:

简而言之,“语言”主要影响缩放的垂直轴,但并非您在问题中已经回避的所有方面。这里有两件事:

1) 需要根据有形指标来定义可扩展性。我建议

S = 用户数/费用

如果没有足够的定义,我们将讨论这一点ad vitam eternam。使用我提出的定义,比较系统实现变得更容易。为了使系统具有可扩展性(阅读:盈利),那么:

可扩展性随 S 增长

2) 系统 可以根据 2 个主轴进行缩放:

a) 垂直 b) 水平

a) 垂直扩展与增强孤立的节点有关,即更大的服务器、更多的 RAM 等。

b) 水平缩放涉及通过添加节点来增强系统。这个过程涉及更多,因为它需要处理现实世界的属性,例如 光速(延迟)、对分区的容忍度、多种故障等。

(节点=>物理分离,不同的“命运共享”)

不幸的是,可扩展性这个词经常被滥用


人们将语言库和实现混淆了太多次。这些都是不同的东西。使一种语言适合特定系统的原因通常更多地与围绕所述语言的支持有关:库、开发工具、实现的效率(即内存占用、内置函数的性能等) .)

在 Erlang 的情况下,它恰好被设计为具有现实世界的限制(例如分布式环境、故障、可用性需求以满足违约赔偿风险等)作为输入要求。

不管怎样,我可以在这里持续太久。

【讨论】:

【参考方案7】:

除了这里提到的关于 Erlang 的观点(我不知道)之外,从某种意义上说,某些语言更适合脚本和较小的任务。

像 ruby​​ 和 python 这样的语言有一些非常适合原型设计和创造力的功能,但对于大型项目来说却很糟糕。可以说,它们的最大特点是缺乏“形式化”,这在大型项目中会伤害您。

例如,静态类型对于小型脚本类型的东西来说是一件麻烦事,并且使像 java 这样的语言非常冗长。但是在具有数百或数千个类的项目中,您可以轻松地看到变量类型。将此与可以保存异构集合的映射和数组进行比较,作为一个类的消费者,您不能轻易地分辨出它持有什么样的数据。随着系统变得越来越大,这种事情变得更加复杂。例如您还可以做一些非常难以追踪的事情,例如在运行时动态地向类添加位(这可能很有趣,但如果您试图找出一条数据的来源,那就是一场噩梦)或调用引发的方法不被编译器强制声明异常的异常。并不是说您无法通过良好的设计和规范的编程来解决这些问题 - 只是更难做到。

作为一种极端情况,您可以(抛开性能问题)使用 shell 脚本构建一个大型系统,并且您可以通过非常处理一些混乱、缺少类型和全局变量的问题 对编码和命名约定严格而谨慎(在这种情况下,您会“按照约定”创建一个静态类型系统),但这不是一个有趣的练习。

【讨论】:

Erlang 是动态类型的,当然可以构建大型系统。【参考方案8】:

Erlang 的核心是基于异步通信(用于协同定位和分布式交互),这是平台实现可扩展性的关键。您可以在许多平台上使用异步通信进行编程,但是 Erlang 语言和 Erlang/OTP 框架提供了使其易于管理的结构——无论是在技术上还是在您的头脑中。例如:如果没有 erlang 进程提供的隔离,您将自取其辱。使用链接/监视器机制,您可以更快地对故障做出反应。

【讨论】:

以上是关于语言和可扩展性之间的关系的主要内容,如果未能解决你的问题,请参考以下文章

博客园markdown语法扩展

UML语言体系

UML语言体系

Mongodb入门

MongoDB礼包|介于关系数据库和非关系数据库之间

系统可扩展设计方法之消息队列