VOL.I · 接诊CH 01监护 01/13

九个九的传说:为什么是 Elixir

传说里有一台电话交换机,可用性 99.9999999%——九个九,折合每年停机 31.5 毫秒。你眨一次眼的工夫,够它「宕机」十年。这个数字真假存疑,但它背后的问题是真的:怎么造一个不允许停下来的系统?这本书讲的语言,就是从这个问题里长出来的。而你翻开这本书的理由——传说中「连进正在运行的系统里 Debug」——正是这个问题的答案之一。

AXD301BEAMlet it crashElixir 1.20

一个不合理的数字

先把这个数字掰开。「几个九」是可用性行业的行话:两个九(99%)意味着每年停机 3.65 天,三个九是 8.76 小时,你手机里大部分 App 的后端能做到四个九(52.6 分钟)就可以开庆功会了。而九个九——99.9999999%——折合每年 31.5 毫秒。一次 GC 停顿都不止这个数。

传说的出处是这样的:1998 年,爱立信发布了 ATM 交换机 AXD301,核心软件用一门自家发明的语言写成,超过一百万行代码。后来这门语言的作者之一 Joe Armstrong 在演讲里引用了一份报告:英国电信的一组 AXD301 节点在多年试运行中,达到了九个九的可用性。

这个数字后来被反复质疑——有人指出它可能只统计了软件层故障,或者只覆盖了某段考核窗口,Armstrong 本人也承认细节已不可考。所以本书把它诚实地称为「传说」。但传说的价值从来不在数字本身:它告诉你,有一个行业把「永不下线」当作目标函数,并且为此造了一整套专用工具。电信行业对停机的容忍度,和医院对心跳停止的容忍度是同一个量级——这也是本书视觉上把系统当「病人」监护的原因。

1986 年的需求清单,恰好是今天的需求清单

把时间拨回 1986 年,爱立信计算机科学实验室。Joe Armstrong、Robert Virding、Mike Williams 三个人接到的任务是:为下一代电话交换机找(或者造)一门合适的编程语言。电话交换机的需求清单长这样:

  • 海量并发——一台交换机同时挂着几十万路通话,每一路都是独立的活动;
  • 故障隔离——某一路通话的软件出错,绝不能挂断另外几十万路;
  • 永不停机——升级软件也不许挂电话,系统必须在运行中换代码;
  • 分布式——多块板卡、多台机器天然协同,单点坏了别处顶上;
  • 软实时——响应要稳定及时,不能被某个全局暂停卡住。

现在把「通话」换成「WebSocket 连接」「聊天会话」「游戏对局」「IoT 设备」,再读一遍这张清单——这就是 2026 年任何一个实时后端的需求清单,一字不用改。这就是 Erlang 押中的赌注:电话交换机的问题,后来变成了整个互联网的问题。

这门语言叫 Erlang(致敬丹麦电信数学家 Agner Krarup Erlang,也可以读作 "Ericsson Language"),1998 年开源。它编译到一台专用虚拟机上运行,这台虚拟机叫 BEAM(Bogdan/Björn's Erlang Abstract Machine,以两任作者命名)。记住 BEAM 这个名字,整本书里它才是真正的主角——Elixir 的所有「超能力」,一半以上其实是 BEAM 的超能力。

▸ Android 类比

BEAM 之于 Elixir,粗看像 ART/JVM 之于 Kotlin:一台带 GC 的字节码虚拟机。但更准确的类比是一个微型操作系统:它有自己的「进程」、自己的抢占式调度器、自己的进程间通信,甚至自己的「任务管理器」(第 11 章的 :observer)。JVM 上你写的是「一个进程里的多线程程序」,BEAM 上你写的是「一个操作系统里的多进程系统」——这个视角差异,是后面所有章节的总纲。

三张王牌:BEAM 的世界观

四十年演化下来,BEAM 的设计可以压缩成三张王牌。本书的三卷正文,就是按这三张牌展开的。

王牌一:进程,但不是你认识的那种进程

BEAM 的「进程」不是操作系统进程,也不是线程:它是虚拟机自己管理的执行单元,初始内存只有约 2.6 KB,创建耗时微秒级。一台普通服务器上跑几百万个进程是常规操作,不是极限测试。更关键的是隔离性:每个进程有自己独立的堆、独立的垃圾回收,进程之间不共享任何内存,只能靠发消息通信。一个进程崩了,堆直接回收,其他进程连一次 GC 停顿都感受不到——这就是「一路通话崩溃不影响其他几十万路」在虚拟机层面的兑现。VOL.III 的四章全部围绕这张牌。

王牌二:崩溃是常态,恢复是设计

大多数语言的哲学是「不要让程序崩溃」,于是代码里长满防御性的 try/catch 和判空。Erlang 的哲学反过来:崩溃是正常生理现象,就像细胞凋亡。你不在业务代码里到处接异常,而是让出错的进程干脆地死掉,由专门的「监督者」进程按预先声明的策略把它重启回干净状态。这套打法有个响亮的名字:let it crash。它不是摆烂,而是一整套被称为 OTP 的工程框架,第 9 章会让你亲手杀进程、看监督树把系统救回来。

王牌三:系统活着的时候,你可以进去看

这张牌就是你来的原因。因为整台虚拟机从第一天起就是为「永不下线」设计的,「排查问题」这件事也必须在不停机的前提下完成。于是 BEAM 给了你:随时连进生产节点的远程 shell、掏出任意进程当前内部状态的 :sys.get_state、图形化的整机监护仪 :observer、能限速防打挂的生产追踪,以及运行中换代码的热加载。在别的技术栈里要靠 APM 厂商、埋点和重启祈祷解决的问题,这里是虚拟机的内置功能。VOL.IV 三章,专门兑现这张牌。

Elixir:同一台引擎,新的驾驶舱

那 Elixir 是什么?2012 年,Rails 核心成员 José Valim 在研究并发问题时发现了 Erlang 和 BEAM,结论大致是:引擎无可挑剔,驾驶舱不太行——Erlang 语法古奥(逗号句号分号各有语义),工具链也停留在上个时代。于是他在 BEAM 上造了一门新语言:

  • 语法友好——借鉴 Ruby 的表达力,但骨子里是彻底的函数式;
  • 元编程——Elixir 大部分语言结构本身就是宏,库作者能造出像语言内置一样的 DSL;
  • 现代工具链——构建工具 mix、包管理 hex、文档系统 ExDoc、自带测试框架 ExUnit,全部开箱即用;
  • 零成本互操作——Elixir 编译成和 Erlang 完全相同的字节码,调用 Erlang 库不需要任何桥接:写下 :timer.sleep(1000) 就是在调 Erlang 标准库,没有性能税。

战绩方面:WhatsApp 用 Erlang 支撑 9 亿用户时工程师只有 50 来人,单机 200 万 TCP 连接的实验记录流传至今;Discord 用 Elixir 扛住了数百万人同时在线的实时消息;Pinterest、Heroku、PagerDuty 的关键链路上都有 BEAM。这些公司选它的理由高度一致:同样的实时并发负载,BEAM 方案的机器数量和团队规模都小得反常。

2026 年的体检报告

你现在学 Elixir,赶上的是一个「核心稳定、短板补齐」的窗口期。给这门语言做个当下的体检:

体征项读数说明
语言版本v1.20(2026 年 6 月)官方正式称 Elixir 为「渐进类型语言」
类型系统集合论类型,推断为主不改代码就能查出大量类型错误,多年最大短板补上了
运行时Erlang/OTP 28新增优先级进程消息等特性,BEAM 仍在快速演进
Web 框架Phoenix 1.8 + LiveView成熟稳定,LiveView 是「只有 BEAM 做得优雅」的技术(第 13 章)
语言核心已宣布基本定型创新转向库层面——现在学,不会过时
就业市场小而精,国内尤小诚实提醒:学它图的是能力模型,不是换赛道

最后一行值得展开说一句:这本书不向你承诺「学完好找工作」。它承诺的是,进程隔离、监督树、消息传递这套思维模型,会永久改变你设计任何并发系统的方式——哪怕你之后写的是 Kotlin 协程。而在线 Debug 的体验,会重新校准你对「可观测性」三个字的期待值。

接电:十分钟环境

macOS 下最省事的是 Homebrew,一条命令同时装好 Erlang 和 Elixir:

$ brew install elixir
$ elixir -v
Erlang/OTP 28 [erts-16.0] [smp:8:8] [ds:8:8:10] [async-threads:1]
Elixir 1.20.0 (compiled with Erlang/OTP 28)

如果你习惯管理多版本(像 Android 侧的多 JDK),社区标准做法是 asdf(或其继任者 mise):先装 erlang 插件再装 elixir 插件,版本写进 .tool-versions。初学阶段 brew 足够,别在环境上消耗热情。

装好之后,输入三个字母,这本书的主舞台就亮了:

$ iex
Erlang/OTP 28 [erts-16.0] [smp:8:8] [ds:8:8:10]
Interactive Elixir (1.20.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 1 + 1
2
iex(2)> "九个" <> "九"
"九个九"
iex(3)> h Enum.map
                          def map(enumerable, fun)

  @spec map(t(), (element() -> any())) :: list()

Returns a list where each element is the result of invoking fun on
each corresponding element of enumerable. ...

注意第三条:h 直接在 shell 里打出了标准库的完整文档。文档是 Elixir 的一等公民,它编译进字节码,跟着代码走到任何地方——包括你将来连进的生产节点。这个细节是一个预告:这门语言生态里,「运行中的系统能告诉你关于它自己的一切」是贯穿始终的设计信条。

⚠ 病历警示

退出 iex 是新手第一个小坑:它是 Ctrl+C两次(或按一次后输入 a 回车),不是 Ctrl+D。另外,Elixir 版本和 Erlang/OTP 版本是两个独立的号,报 bug 或查兼容性时两个都要说——elixir -v 的输出里两者都有。本书代码在 Elixir 1.20 / OTP 28 上验证,但除了明确标注的新特性,绝大多数内容在 1.14+ 都成立。

本书的查房路线

全书 13 章,按四个病区推进:

  • VOL.I 接诊(本章)——你已经知道这台机器为何而造;
  • VOL.II 解剖(02–06)——语言本身:类型、模式匹配、函数与管道、不可变数据、控制流。五章打完,你能流畅读写 Elixir;
  • VOL.III 心跳(07–10)——并发核心:进程、GenServer、监督树、并发工具箱。这是 OTP 的心脏,也是理解「九个九」的全部机制;
  • VOL.IV 查体(11–13)——兑现传说:连进活节点做体检、生产级追踪与热加载、生态地图。

如果你只有一个晚上,想先尝到最甜的部分:走 01 → 03 → 07 → 09 → 11 这条快速路径,五章体验完「模式匹配 → 进程 → 监督树 → 在线 Debug」的主动脉,再回头补全。

✚ 动手自测
  1. 算一算:四个九(99.99%)的系统每年允许停机多少分钟?九个九呢?体感一下差距。
  2. 装好环境,进 iex,跑通 1 + 1h String.split,然后用 Ctrl+C 两次体面退出。
  3. 思考题:你手头业务里,哪个系统最接近「电话交换机式」的需求(海量长连接 + 故障隔离 + 不能停)?带着它往下读。

接诊完毕。下一章进解剖室,从 iex 和基本类型开始——你会发现这门语言的「原子」比你想象的可爱得多。