观看 Saša Jurić 在 GOTO 2019 的演讲 “The Soul of Erlang and Elixir”,有用的入口,是先把惯常的语言比较冲动放慢。开场不用落在模式匹配、管道语法、Ruby 影响,或 Phoenix 应用是否比另一套 Web 技术栈更合口味。Jurić 的演讲有更深的一层。它提出的是系统问题:当并发、隔离、恢复与观察成为运行时模型的一部分,而不是每个服务里重新拼出来的库约定,系统会发生什么变化?[1][2]

这也解释了为什么这场演讲在 2026 年仍然是一件有效的 OSS 材料。GOTO 的会议页面把它定位为一场解释 Erlang 与 Elixir 为什么适合服务端系统的演讲,尤其是高可用场景;页面同时说明,重点落在并发模型,而不是语法展示或生态浏览。[2] Elixir 自己的首页也用压缩的方式讲了同一件事:Elixir 运行在 Erlang VM 之上,而 Erlang VM 以低延迟、分布式、容错系统闻名,同时给开发者提供适用于 Web、嵌入式、机器学习、数据与多媒体工作的高生产力语言表面。[3] 有意思的主张,并非 Elixir 在 Erlang 之上加了一层漂亮语法。真正的主张是,它把另一套故障词汇放到了普通应用代码附近。

这支视频值得加注观看,因为 Jurić 通过一个运行中的系统来讲这套词汇,而不是罗列功能清单。把演讲、官方文档与演示仓库放在一起看,我从中得到的核心经验是进程形态。Elixir 应用在工作被拆进许多隔离进程、通信通过显式消息流动、故障被交给知道该重启哪些部分以及何时停止尝试的监督者之后,会变得更容易推理。[1][4][5][6]

配图说明:封面使用 José Valim 在 ElixirConf 2014 的真实照片,而不是 logo、图表或生成的运行时抽象。本文讨论的是一个语言社区如何把 BEAM 的操作性思想带进日常应用设计,因此真实会议照片更贴合这个视觉层次。[7]

演讲开头,“进程”就是设计单位

首先值得看的,是这场演讲多快离开语言装饰,转向进程行为。Elixir 官方进程指南直接给出基础:所有代码都运行在进程内部;这些进程彼此隔离;它们并发运行;并通过消息传递通信。[4] 文档也提醒读者,不要把它们和操作系统进程混在一起,因为 BEAM 进程足够轻量,可以同时运行数万甚至数十万个。[4]

这个细节会改变设计压力。在许多主流技术栈里,创建一个新的并发单位显得成本很高,开发者常把互不相关的工作集中进共享服务,随后用架构的剩余部分去管理锁、回调、队列与局部故障。在 Elixir 与 Erlang 里,一个进程可以成为围绕一项任务、一个连接、一次会话、一片状态或一条受控工作流的普通边界。进程不只是调度技巧。它是状态、邮箱、故障与内省相遇的单位。

这也是演讲里的演示比抽象推介更有说服力的原因。Jurić 展示的并非通常营销语境里的“并发很容易”。他展示的是,运行时给工程师许多可以安放责任的小位置。系统不再像一个巨大的可变对象,而更像一片由隔离 actor 组成、通信路径清楚的场域。[1][4]

到消息传递段落,邮箱就是契约

下一个重要观念是,消息传递的意义不只在避免共享内存。Elixir 指南说,send/2 会把一条消息放进接收方邮箱,发送方继续执行;接收方随后在自己的邮箱中寻找匹配模式。[4] 这意味着每个进程都拥有自己的状态,也拥有自己准备处理的消息形态。

这是一份有力量的契约,但没有魔法。一个进程如果被要求串行化过多工作,邮箱仍会变成瓶颈。进程太慢,或接收逻辑无法匹配系统其他部分发来的内容,消息仍会堆积。因此,视频传递的经验比“到处使用进程”更窄,也更实用:在进程能够形成有意义的故障边界、所有权边界或背压边界时使用它。进程便宜到可以成为设计原语,但仍然需要一份明确职责。

演示仓库也强化了这种实践读法。它不是精修过的框架样例;README 描述的是一个小系统,里面有负载控制 dashboard、求和计算页面、额外节点,以及在系统运行时升级选定模块的方法。[6] 这个形态很重要,因为这场演讲讨论的是在线操作行为。重点不在于每个应用都照抄这个演示。重点在于,BEAM 系统会自然引出这些问题:哪个进程正忙,哪条消息路径过载,哪个组件可以在不把整个服务变成单一错误域的情况下被重启或升级。[1][6]

到故障段落,“let it crash”意味着计划好的恢复,而不是漠然放任

“let it crash” 这个短语很容易被误用。弱化之后,它听上去像懒惰:少写错误处理,然后寄望一次重启把事情修好。官方进程指南讲得更精确。它说,隔离进程里的失败默认不会破坏其他进程的状态,链接让进程之间建立失败关系;多数情况下,进程会链接到监督者,由监督者发现死亡并启动替代进程。[4]

Erlang 的监督者文档补上了缺少的操作纪律。监督者会启动、停止并监控子进程,基本目标是在必要时通过重启让子进程保持存活。[5] 文档还定义了 one_for_oneone_for_allrest_for_one 等重启策略,并包含重启强度与周期设置,让监督者能够停止重启风暴,而不是不断重复一场已经注定失败的恢复。[5]

这就是对 Jurić 论点的成熟读法。容错并不等于失败消失。它意味着失败带有隔离、名称、限度与升级路径。一个子进程死亡时,one_for_one 监督者可以只重启该进程。一个群组存在结构性依赖时,另一种策略可以重启更大的集合。失败过快重复时,监督者放弃,并让下一层决定。[5] 相比在请求路径外套一层宽泛的 try/catch,这是一套丰富得多的模型。它把恢复变成拓扑。

持久的经验,是一份边界预算

这场演讲最强的工程启发在于,Elixir 与 Erlang 给团队提供了另一种边界预算。团队可以较自由地使用进程来隔离工作,使用监督者来编码恢复关系,使用消息传递来让所有权显形。这不会取消架构。它让架构在运行时变得可见。

这也标出了采用边界。如果一个应用主要是批处理计算,失败语义又很简单,BEAM 模型未必会成为决定性优势。如果系统长期运行、连接密集、交互频繁、带有状态,或在操作层面对局部故障高度敏感,进程与监督模型就会变得更有意思。问题不该停在“每个后端都应该使用 Elixir 吗?”更好的问题是:“这类工作负载是否受益于把故障、状态与并发当成一等运行时对象来处理?”[3][4][5]

Jurić 的演讲之所以仍有价值,是因为它让这个问题保持具体。看视频时可以关注演示,同时把书面模型放在心里:轻量隔离进程让责任变小;邮箱让通信显式化;监督者让重启策略成为系统树的一部分;重启强度防止恢复本身变成拒绝服务。这里就是运行时论证的核心。语法在表面。故障边界才是产品。[1][4][5][6]

来源

  1. GOTO Conferences,“The Soul of Erlang and Elixir - Sasa Juric - GOTO 2019”,YouTube 视频,录制于 GOTO Chicago 2019。
  2. GOTO Chicago 2019,“The Soul of Erlang and Elixir”——会议页面,包含演讲定位、受众与并发模型重点。
  3. Elixir,“The Elixir programming language”——Elixir 运行于 Erlang VM 之上的官方概览,涵盖容错系统、平台特性与 Erlang 兼容性。
  4. Elixir documentation,“Processes”——进程隔离、轻量并发、邮箱、链接、任务、状态,以及面向监督的故障处理。
  5. Erlang/OTP documentation,“Supervisor Behaviour”——监督原则、重启策略、子进程规格与重启强度限制。
  6. Saša Jurić,sasa1977/soul_of_erlang_and_elixir——这场演讲的演示源码仓库,包含负载控制 dashboard、release、节点与升级说明。
  7. Wikimedia Commons,“File:José Valim - elixirconf-43 (14795675701).jpg”——Augie De Blieck 拍摄的 2014 年 ElixirConf 真实照片,作为本文图片来源。