了解长生不老药

Understanding Elixir

Java 像 SpringBoot 和 Play 这样的框架可以利用机器上的所有内核同时处理多个请求吗?如果他们能做到这一点,那么 Elixir 在并发方面解决了什么独特的问题?这只是一种不同的语言吗?或者计算范式本身有什么不同,这让我们使用 Elixir 降低了服务器成本?

报告如下: https://www.techworld.com/apps-wearables/how-elixir-helped-bleacher-report-handle-8x-more-traffic-3653957/

问题

在多核上处理多个请求的困难之一是您现在同时拥有程序的多个部分 运行 并修改状态。所以互斥锁出现了,你现在不得不担心一个函数是否是线程安全的,并管理 blocking/waiting/resuming 锁和互斥锁。这意味着您现在有一堆潜在的瓶颈,并且会增加成本并减少(甚至消除)您期望从并发中获得的收益。

向左进入舞台

Erlang 和 Elixir 具有基于 'Actor Model' 的并发性。参与者接收消息并从这些消息中创建新消息、确定如何处理下一条消息、修改自己的内部状态或创建更多参与者。归结为每个 Actor 都有自己的状态,因此您不再需要担心锁或互斥量。这有助于减轻编写并发代码的心理负担,并且作为一种抽象,可以更轻松地推理并发性。结果是更容易维护的更有效的并发代码。 Actor 模型可以用任何语言实现,但它已融入 Elixir 和 Erlang。

此外,Elixir 和 Erlang 都是函数式语言。在函数式语言中,副作用、来自先前方法调用的挥之不去的状态通常是可以避免的。这意味着只要您不违反功能纯度,您就可以说一个方法每次都会 return 做同样的事情。这意味着 f(x) + f(x) == f(x)*2。这意味着您可以毫无畏惧地更大程度地并行化事物。 运行 一个线程上的 f(x) 和另一个线程上的 g(x) 如果它们在功能上是纯的,则不会导致竞争条件。因为它们都不影响状态。

主要有以下三个区别。

轻量级进程

Elixir’s processes should not be confused with operating system processes. Processes in Elixir are extremely lightweight in terms of memory and CPU (even compared to threads as used in many other programming languages). Because of this, it is not uncommon to have tens or even hundreds of thousands of processes running simultaneously. — https://elixir-lang.org/getting-started/processes.html

容错

Erlang 因此 Elixir 使用 监督树 的概念,确保崩溃单个进程不会对整个应用程序造成任何损害;更重要的是,Erlang 哲学明确地将“让它崩溃”作为其座右铭。这基本上意味着您不应该在某种程度上关心错误处理。

The supervisors have a built-in mechanism to limit the number of restarts which can occur in a given time interval. This is determined by the values of the two parameters MaxR and MaxT. If more than MaxR number of restarts occur in the last MaxT seconds, then the supervisor shuts down all the children it supervises and dies. — http://erlang.org/documentation/doc-4.9.1/doc/design_principles/sup_princ.html

消息传递

Erlang 进程完全隔离,而不是数据共享,唯一的交换机制是传递消息;这使得内存管理非常有效和高效。

Each process has its own input queue for messages it receives. New messages received are put at the end of the queue. When a process executes a receive, the first message in the queue is matched against the first pattern in the receive. If this matches, the message is removed from the queue and the actions corresponding to the pattern are executed. — http://erlang.org/doc/getting_started/conc_prog.html#message-passing

我会尽量给出一个简短的答案: Erlang/Elixir 强制 你使用 actor 编写异步、非阻塞代码。

您可以将参与者视为 "micro"-微服务。 Actor 有自己的状态,他们不共享数据(实际上所有变量都是不可变的)因此需要在彼此之间传递异步消息。

Erlang/Elixir 有自己的虚拟机可以处理数十万线程和其他优化(线程不是操作系统线程,而是 virtual/lightweight 并且虚拟机避免在内存中复制数据即使所有变量都是不可变的,仅在真正需要时才复制数据)

我不是最新的其他 Actor Systems,所以我不能说例如。 JVM 上的 Akka 将获得类似的性能。 Virtual Actor Systems 可能还可以处理很多工作,Orleans 就是这样一个框架。 (虚拟演员系统可以比作演员的依赖注入,当你需要他们时,他们被创建,当他们不被使用时,他们从内存中删除)https://github.com/dotnet/orleans