插件窝 干货文章 为什么 Elixir 在异步处理方面比 Nodejs 更好?

为什么 Elixir 在异步处理方面比 Nodejs 更好?

并发 操作 异步 进程 697    来源:    2024-10-21

简单答案:Node.js 是单线程的,并拆分该单线程来模拟并发,而 Elixir 利用 Erlang 虚拟机 BEAM 的原生并发和并行性来同时运行进程。

下面,我们将更深入地理解这种差异,探索两个关键概念:Node.js 事件循环和 Elixir 的 BEAM VM 和 OTP。这些元素对于理解每种技术如何处理执行异步任务以及这如何影响不同应用程序中的性能和可扩展性至关重要。

1.什么是事件循环?

Node.js 在单个主线程上运行,并使用称为事件循环的机制来管理异步操作。基本概念是它检查待处理的待处理任务,例如 I/O 操作、promise 和 callbacls,并在它们准备好时执行它们。

1.1 实践中如何运作:

当发起异步操作时(例如对 API 的查询),它会被委托给 libuv。同时,事件循环继续接受其他连接。
当异步操作完成时,libuv 将结果返回到事件队列,然后事件循环将与该操作关联的回调放在调用堆栈上。

1.2 事件循环的局限性:

  • 如果调用堆栈上存在耗时或 CPU 密集型任务,它可能会阻塞其他操作的处理,从而降低效率。

  • 并发性受到限制,因为所有内容都在单个主线程上运行。

2.BEAM VM 和 OTP

Elixir 构建于 BEAM VM 之上,该虚拟机为 Erlang 提供支持,以其处理高并发性和弹性的能力而闻名。与 Node.js 不同,Elixir 不依赖于单个线程。相反,它使用由 BEAM 管理的极其轻量级且隔离的进程。

2.1 实践中如何运作:

  • Elixir 中的每个进程都是独立的,这意味着它们不共享内存,也不会互相阻塞。
  • 这些进程由 BEAM 管理,BEAM 可以同时创建和管理数百万个进程,在所有可用的 CPU 核心之间分配负载。
  • 此外,Elixir 还附带 OTP(开放电信平台),它提供了一组用于构建强大的分布式系统的库和工具。

2.2 BEAM和OTP的优点:

  • 可扩展性:BEAM 可以将进程分布在所有 CPU 核心上,从而最大限度地提高资源利用率。
  • 弹性:如果一个进程失败,不会影响其他进程。这允许您构建容错系统。
  • 真正的竞争:与仅限于单个线程的事件循环不同,Elixir 可以利用多个 CPU 核心真正并行运行进程。

3. Node.js 和 Elixir 的实践比较

让我们想象一台服务器需要处理数千个同时连接,每个连接都执行异步操作和一些繁重且耗时的处理。

3.1 使用 Node.js:

  • 服务器在某种程度上是高效的,但随着大量操作的堆积,事件循环开始变得过载。尽管充分利用 JSNode 中可用的资源可以对性能有很大帮助:例如正确使用 async/wait 和/或 then/catch 以及内置资源例如lib节点:集群
  • 这可能会导致响应新连接的延迟,从而对性能造成重大影响。

3.2 使用 Elixir:

  • 每个连接都可以由单独的进程管理。 I/O 操作、计算甚至故障都可以隔离管理。
  • BEAM 有效地分配负载,确保系统即使在高需求下也能继续运行而不会出现重大问题。
  • 如果需要,可以通过消息在进程之间进行通信。
  • BEAM 抢占式调度引擎。

结论

Node.js 对于许多应用程序来说都是一个出色的工具,特别是那些处理简单异步操作并且不需要大量 CPU 处理的应用程序。然而,其基于单线程的并发模型在更复杂的场景中可能会成为瓶颈。

Elixir 具有 BEAM VM 以及对轻量级进程和大规模并发的本机支持,为需要处理大量并发操作并在多个 CPU 线程之间分配负载的系统提供了强大且高效的替代方案。如果您需要弹性、可扩展性和高并发,Elixir 是您的选择。

虽然本文的标题大胆地表明 Elixir 和 BEAM 在异步处理方面优于 Node.js,但重要的是要认识到这些技术之间存在显着差异。决定使用哪一种必须考虑多种因素,而不仅仅是这里讨论的并发性和并行性。生态系统、团队对语言的熟悉程度、具体项目要求以及要执行的任务的性质等方面在选择最适合工作的工具时发挥着至关重要的作用。毕竟每个场景都有其特殊性,技术的选择必须从全局的角度考虑,考虑到项目的所有需求和挑战。

参考书目

主题:

线程是程序中最小的执行单元。在许多操作系统上,一个进程可以包含多个线程,每个线程执行程序的不同部分。线程可以共享内存和资源,但这可能会导致并发问题,例如竞争条件。

竞赛:

并发是系统同时处理多个任务的能力。在并发系统中,多个任务即使不同时运行也可以独立进行。例如,BEAM 管理独立运行的竞争流程。

事件循环:

事件循环是 Node.js 等系统中用于管理异步操作的设计模式。它工作在单线程中,循环执行任务,响应I/O和异步执行等事件,保证程序在等待长时间操作的同时继续响应。

并行度:

并行是指在不同的CPU核心上同时执行多个任务。与指并发任务的管理的并发不同,并行涉及实​​际上同时执行这些任务。 BEAM 将进程分布在多个核心上以最大限度地提高并行性。

轻量级流程:

在 BEAM 中,轻量级进程是比传统线程具有更高内存和 CPU 效率的执行单元。它们彼此隔离并由 BEAM 管理,这允许您创建和管理数百万个并发进程。

抢占式调度:

抢占式调度是一种运行时管理系统,操作系统或虚拟机为每个进程分配时间片,确保没有进程独占CPU。在 BEAM,这确保了所有流程都有机会公平执行。

光束虚拟机:

BEAM(Bogdan/Björn 的 Erlang Abstract Machine)是运行 Erlang 和 Elixir 代码的虚拟机。它以高效管理轻量级流程、支持大规模并发和并行以及提供容错能力而闻名。

OTP(开放电信平台):

OTP 是 Erlang 和 Elixir 附带的一组库和设计模式。它提供了构建并发、分布式和容错系统的工具,有助于开发健壮且可扩展的应用程序。

利布夫

是一个跨平台库,为 Node.js 中的异步 I/O 操作提供支持。它负责实现事件循环并抽象操作系统功能,例如网络操作、文件系统和线程。 libuv 允许 Node.js 在单线程中高效执行异步任务,利用内部线程池进行阻塞操作,确保主事件循环的连续性。

输入/输出操作

I/O(输入/输出)操作是指程序与外部世界之间的任何交互,例如读取或写入文件、与硬件设备通信或通过网络交换数据。这些操作可能非常耗时,并且在许多系统上都是异步执行的,以防止程序在等待操作完成时发生阻塞。

参考

二郎。简短的 BEAM 底漆。 Erlang 博客,2020。可访问:https://www.erlang.org/blog/a-brief-beam-primer/。访问日期:8 月 29 日2024 年。

二郎。 Erlang 入门 [PDF]。 Erlang.org。网址为:https://erlang.org/download/erlang-book-part1.pdf。访问日期:8 月 29 日2024 年。

节点医生。 Node.js 事件循环的动画指南。 Dev.to,2021 年。可访问:https://dev.to/nodedoctors/an-animated-guide-to-nodejs-event-loop-3g62。访问日期:8 月 29 日2024 年。

节点医生。动画 Node.js 事件循环阶段。 Dev.to,2022 年。可访问:https://dev.to/nodedoctors/animated-nodejs-event-loop-phases-1mcp。访问日期:8 月 29 日2024 年。

NODE.JS。簇。 Node.js,2023。可在:https://nodejs.org/api/cluster.html 获取。访问日期:8 月 29 日2024 年。