2021 年你需要知道的关于 Erlang 的一切
- admin
- 07 Jan 2024
今天,我们将看一个相当古老且有些古怪的东西。 你们大多数人可能没有注意到的语言。 虽然 Erlang 不像某些现代编程语言那样流行,但它安静地运行着 WhatsApp 和微信等每天为大量用户提供服务的应用程序。 在这篇文章中,我将告诉你关于这门语言的更多事情、它的历史,以及你是否应该考虑自己学习它。 ## 什么是 Erlang,它在哪里使用? Erl
Read MoreErlang通常被称为“面向并发的编程语言”。
这个名字是怎么来的?80 年代为电信行业创建的语言现在如何帮助我们?
Erlang 或Erlang/OTP(开放电信平台)于 1986 年在爱立信构建,用于处理不断增长的电话用户群。它的设计目的是改进电话应用程序的开发。它非常适合构建分布式、容错和高可用性系统。
那么,80 年代建立的技术对我们有什么意义呢?事实证明,构建 Erlang 的所有原因对于当今的 Web 服务器都非常有用。我们需要快速可靠的应用程序——我们现在就需要它!幸运的是,Phoenix 可以做到这一点,它是建立在 Elixir(一种建立在 Erlang 之上的现代语言)之上的 Web 框架。它具有强大的功能,例如热代码交换和实时视图– 无需 JS!
这怎么可能?这主要是由于该语言是如何构建为自下而上并发的 – 这要归功于 Erlang VM BEAM。
在我们继续之前,让我们回顾一下 CS 101 中的一些概念 – 操作系统!
为了理解为什么 Erlang VM 中的并发性如此特殊,我们需要了解操作系统如何处理指令/程序。CPU负责机器的工作。它的唯一责任是执行流程。现在一个进程是一个孤立的执行块。它们有自己的内存、上下文和文件描述符。然后,一个进程由许多线程或过度使用的定义“轻量级进程”组成。
CPU 负责以最有效的格式执行这些进程。它可以一个接一个地执行进程。这被称为“顺序执行”。这是最基本和最古老的执行方法。今天对我们来说也没什么用——即使是我们今天使用的冰箱也可以做得更多!
您可能会认为,由于现代计算机允许您一次做不止一件事,CPU 正在并行执行进程。像这样的东西,
然而,这远非事实。我们离真正的并行性还很远。当我们尝试走这条路时,会出现大量的错误。
下一个最好的事情是并发。并发执行意味着将进程分解为多个微小位并在执行时在它们之间切换。这种情况发生得如此之快,以至于它给用户一种同时执行多个进程的错觉——同时巧妙地规避了并行性问题。
它看起来像这样,
您可以看到 CPU 在进程 1 和 2 之间切换。这称为上下文切换。这是一项相当繁重的任务,因为 CPU 需要在切换之前将进程的所有信息和状态存储到内存中,然后在需要再次执行时将其加载回来。然而,我们已经在这个问题上工作了这么多年,我们已经非常擅长它了。
最后一件事 …
程序执行的速度取决于 CPU 时钟周期。处理器越快,您的计算机就越快。摩尔定律指出,负担得起的 CPU 上的晶体管数量每两年就会翻一番。但是,如果您注意到,处理器在过去几年中并没有变得那么快。那是因为我们在路上遇到了困难。当我们意识到远高于 4GHz 非常困难且徒劳无功时,就会发生这种情况。光速居然成了约束!这是我们决定改为进行多核处理的时候。我们水平缩放,而不是垂直缩放。我们添加了更多核心,而不是让单个核心变得强大。好的,我知道我们在这里介绍了很多材料。但是你要记住的是上下文切换很繁重,我们现在已经转向多核处理。让我们看看 Erlang 有什么技巧使它成为面向并发的语言。
在上一节中,我们推断并发是实现多处理的最佳方式。多年来,各种语言引入了不同的算法来实现并发。让我们看一下与我们讨论的主题相关的三个最重要的问题。
这是 Java 和 C# 等流行编程语言中常用的模型。它涉及访问同一块内存的不同进程以进行存储和相互通信。它允许上下文切换不那么繁重。虽然这在理论上听起来不错,但当两个或多个进程尝试访问同一个共享内存块时,它会导致一些不友好的情况。它会导致死锁等情况。为了克服这个问题,我们有互斥锁、锁和同步。然而,这使得系统更加复杂并且难以扩展。
这是 Erlang 和 Rust 用来实现并发的模型。它取决于尽可能隔离进程并将它们之间的通信减少为消息传递。每个过程都称为一个参与者。参与者通过发送消息相互通信。消息可以随时发送并且是非阻塞的。然后将这些消息存储在接收参与者的邮箱中。为了读取消息,参与者必须执行阻塞读取操作。
这是 Go 使用的高效并发模型。它与参与者模型相似,因为它使用消息传递。然而,与仅接收消息被阻塞的参与者模型不同,CSP 要求两个动作都被阻塞。在 CSP 中,称为通道的单独存储用于传输传入和传出消息。进程之间不直接通信,而是通过通道层进行通信。
让我们更深入地了解 Actor 模型。
actor是并发的一种原始形式。每个actor都有一个身份,其他actor可以使用它来发送和接收消息。收到消息后,它们将存储在actor邮箱中。actor必须明确尝试读取邮箱的内容。每个actor都与其他actor完全隔离,彼此之间不共享任何内存。这完全消除了对复杂同步机制或锁的需求。
一个actor在收到消息时可以做三件事,
前两个非常简单。让我们看一下它在代码级别上的表示。
您可以在这里看到在 Elixir 中产生一个新进程 – 就是这样。与其他使用多线程方法处理并发的语言不同,Elixir 使用进程。
要记住的关键是,这些是 Erlang 进程,它们比它们的操作系统对应物轻得多。平均而言,Erlang 进程的轻量级是 OS 进程的2000分之一。一个系统中可能有数十万个 Erlang 进程,一切都会好起来的。
Erlang 线程也有一个附加到它们的 PID。使用此 PID,您可以提取有关它占用的内存、它正在运行的功能等等的信息。使用 PID,您还可以向进程发送消息并进行通信。
下面我们来看看一个actor的最终职责——指定。
您可以在这里看到每个进程也有自己的内部状态。每次我们向进程发送消息时,它不会以默认值重新启动函数,而是能够保留正在运行的进程的状态信息。本质上,每个actor都能够访问它自己的内存集,并对其进行跟踪。
这三个属性允许 Erlang 进程非常轻量级。它消除了对复杂同步技术的需求,并且不会为进程间通信占用太多内存空间。所有这些都有助于实现高可用性。
BEAM VM 被构建为能够将Erlang/Elixir 文件编译成字节码 (.beam) 文件以及在 CPU 上调度 Erlang 进程。这种级别的控制为高效和并发运行的进程提供了巨大的优势。
当 VM 启动时,第一步是启动调度程序。它负责在 CPU 上同时运行每个 Erlang 进程。由于所有进程都由 BEAM 维护和调度,因此我们可以更好地控制进程失败时发生的情况(容错),并且能够更有效地使用内存并执行更好的上下文切换。
BEAM 还充分利用了硬件。它为每个可用的核心启动一个调度程序,允许进程自由高效地运行,同时仍然能够相互通信。
保持冷静,我们快结束了!
Erlang 并发很大程度上归功于 Actor 模型和 BEAM。但是,还有许多其他结构也可以确保稳定性。我将为您提供一些小提示,您可以参考它们以供进一步阅读。
有一个特殊的 Erlang 进程称为 Supervisor。它的目标是弄清楚当流程失败时该怎么做。它有助于将其重置为初始值,以便可以再次将其发送以进行处理。
Erlang 的创建者Joe Armstrong,曾说过这句著名的台词“Let it crash”。他设计容错语言的目的不是为了防止错误发生,而是为了构建处理错误场景的结构。
Erlang 使得构建分布式系统变得非常容易。每个 Erlang 实例都可以充当不同设备中的一个节点,并且可以像生成进程一样轻松地进行通信。
与其他解释性语言(最著名的是 Ruby 和 Python)不同,Erlang 不必担心全局解释器锁。GIL 确保 CPU 上每个进程只运行一个 Ruby/Python 线程。这对并发性是一个巨大的打击。像Passenger这样的应用服务器试图通过创建多个操作系统处理器并在多个内核上运行来克服这个问题。然而,正如我们之前看到的,操作系统线程的管理成本很高。
Erlang 是一种结构精美且贯穿始终的语言。它确实经受住了时间的考验,并且在最近变得更加重要。
今天,我们将看一个相当古老且有些古怪的东西。 你们大多数人可能没有注意到的语言。 虽然 Erlang 不像某些现代编程语言那样流行,但它安静地运行着 WhatsApp 和微信等每天为大量用户提供服务的应用程序。 在这篇文章中,我将告诉你关于这门语言的更多事情、它的历史,以及你是否应该考虑自己学习它。 ## 什么是 Erlang,它在哪里使用? Erl
Read More这篇文章探讨了 Erlang/OTP 25 中基于类型的新优化,其中编译器将类型信息嵌入到 BEAM 文件中,以帮助JIT(即时编译器)生成更好的代码。 ## 两全其美 OTP 22 中引入的基于SSA的编译器处理步骤进行了复杂的类型分析,允许进行更多优化和更好的生成代码。然而,Erlang 编译器可以做什么样的优化是有限制的,因为 BEAM 文件必须
Read More自从Erlang 存在,就一直有让它更快的需求和野心。这篇博文是一堂历史课,概述了主要的 Erlang 实现以及如何尝试提高 Erlang 的性能。 ## Prolog 解释器 Erlang 的第一个版本是在 1986 年在 Prolog 中实现的。那个版本的 Erlang 对于创建真正的应用程序来说太慢了,但它对于找出Erlang语言的哪些功能有用,哪
Read More