2026年,程序员圈子里最卷的两件事:一是让AI推理跑得更快,二是让服务在百万并发下不崩。多数人在第一条路上狂奔,却很少有人回头问一句:有没有一套运行时,天生就是为"大量任务同时跑、其中几个发疯也不连累别人"设计的?
有,而且快四十年了。1980年代末,爱立信为了给电话交换机写软件,养了一支小团队专门造语言,造出来的东西叫Erlang,配套的虚拟机叫BEAM。电话交换机是什么概念?七乘二十四小时不能停、成千上万路呼叫同时进行、任何一路出问题都不能影响别人——这几乎是对软件最苛刻的命题。2019年,Elixir社区的老兵Saša Jurić(也是《Elixir in Action》的作者)在GOTO大会上做了一场约45分钟的演讲,标题叫《The Soul of Erlang and Elixir》,把BEAM的灵魂掰开讲透:它不是靠代码写得完美来保证可靠,而是靠"假设每个人都会崩",把每个崩溃关进独立的笼子。
对通信行业的读者,这场演讲还有一层额外的亲切:你每天维护的电信网,正是这门语言出生的理由。这篇解读,带你把这堂课拆开揉碎。
| 项目 | 内容 |
|---|---|
| 视频标题 | The Soul of Erlang and Elixir |
| 演讲者 | Saša Jurić(《Elixir in Action》作者) |
| 频道 | GOTO Conferences(官方频道) |
| 场合 | GOTO 2019 大会 |
| 时长 | 约45分钟 |
| 播放量 | 数十万级(GOTO频道常青热门,撰稿时未逐日核对) |
| 核心主题 | BEAM虚拟机的设计哲学:隔离、抢占、独立GC与"放任崩溃" |
说明:如链接失效,在YouTube搜索标题「The Soul of Erlang and Elixir」即可直达官方版本;播放量等动态数据以频道页面为准。
(视频原话大意)演讲从历史讲起:1980年代,爱立信实验室的Joe Armstrong、Robert Virding、Mike Williams接到的任务,不是"再写一门语言",而是一个业务命题——交换机软件要能扛住海量并发呼叫、故障时不能整机停摆、升级时不能中断服务。语言只是答案,问题才是灵魂。
后来的故事广为流传:Erlang一度在爱立信内部失宠,团队出走,1998年语言开源;而运行Erlang的AXD301交换机,据爱立信宣传口径达到过99.9999999%的可用性——著名的"九个9"。这个数字业内一直有争议,但有一点没人反驳:它是唯一一门把"电信级可用性"写进基因的主流语言。
(我的延伸)注意这个叙事顺序——先有极端场景,后有设计取舍。我们平时评价技术栈,习惯问"性能如何、生态如何",却很少问第三个问题:"它假设世界是善意的,还是恶意的?"BEAM的所有设计,都建立在"世界是恶意的"这个假设上。
(视频原话大意)Saša全场最核心的一个反转是:BEAM的原始计算速度并不出色——纯数值计算它跑不过C,甚至跑不过很多带JIT的现代运行时。但演讲里他反复演示一类场景:一个进程陷入死循环疯狂烧CPU,或者成百上千个进程同时产生垃圾,系统其他部分的响应延迟几乎纹丝不动。
他的总结(大意):大多数运行时优化的是"单个任务跑多快",BEAM优化的是"一万个任务里有一个发疯时,其他九千九百九十九个的延迟"。前者的指标是吞吐,后者的指标是可预测性。
(我的延伸)这个区分在今天的AI基础设施里极其值钱。推理服务的瓶颈常常不是算力不够,而是长尾延迟:某一个批处理任务卡住垃圾回收,所有排队请求的P99一起爆炸。BEAM把"可预测延迟"做成默认属性,这正是它被WhatsApp、Discord这类海量连接业务选中的原因——据其工程博客和媒体广泛报道,WhatsApp曾用少量工程师维护数亿用户,单机并发连接做到百万级。
(视频原话大意)BEAM怎么做到上一条?答案是整个体系的地基:进程隔离。BEAM进程不是操作系统的进程,而是虚拟机里的轻量执行单元,初始只占几KB内存,一台机器开百万个都不吃力。关键在于:每个进程拥有自己独立的堆(heap),进程之间不共享任何可变内存。
这一条设计带来一串连锁反应。第一,崩溃不传染:一个进程的内存坏掉,烂掉的是它自己的堆,别人碰都碰不到。第二,不需要锁:没有共享可变状态,就没有数据竞争,也就不需要用锁把并发度锁死。第三,生命周期互不牵连:进程随时生灭,互不留烂摊子。
(我的延伸)对比一下主流路线:Java的线程共享一个堆,于是整个语言二十年的进化史,一半是在为"共享内存"擦屁股——锁、volatile、内存屏障、并发容器。Go用channel鼓励"不要用共享内存通信",但共享内存依然合法,擦屁股的事一件没少。BEAM是直接在语言层面取消了犯错的可能。这不是API设计,这是宪法设计。
(视频原话大意)隔离的第二个红利是垃圾回收。主流运行时(典型如JVM)的GC演进史,就是一部与"全局暂停"(stop-the-world)斗争史:GC要整理堆,就得让所有线程站住,于是总有一段"全世界一起卡住"的时间。收集器一代代改良,但全局暂停的阴影始终在。
BEAM根本没有全局堆。每个进程GC自己的堆,你回收你的,我回收我的,互不等待。更妙的是:BEAM进程大多短命(处理一个请求、转 发一条消息就结束),进程一死,它整个堆直接整体回收——最好的垃圾回收,是压根不用回收。
(我的延伸)这解释了第2点那个"死循环不连累别人"的演示:GC的成本被切成了百万份小颗粒,每份都小到可以忽略。这其实和硬件设计的思路同构——把大粒度的共享资源拆成小粒度的私有资源,是工程师对抗"一个部件卡住全系统"的万能解法,CPU的核内缓存、5GC的服务化拆分,走的都是这条路。
(视频原话大意)第三个机制是调度器。很多语言采用协作式调度:任务自己跑,跑到"让出点"(比如IO等待)才交出CPU。这个模型的命门是——一个不配合的任务能把整个调度器饿死。Node.js的事件循环被一个同步大循环堵死,是无数事故的经典剧本。
BEAM是抢占式的:每个进程领到一小段执行预算(按"归约次数"计,每个时间片大约几千次归约),用完强制让位,下一个进程上台。没人能多占一纳秒。所以演讲里那个死循环进程才那么安详——它发它的疯,调度器每过一小片就把它拎下去,别人的活照干。
(我的延伸)两点公道话必须说。其一,BEAM的抢占也有例外:调用C扩展(NIF)时调度器可能被阻塞,所以Discord当年要用Rust写NIF做热路径时,社区专门讨论过这个问题——世上没有免费的完美。其二,这个思路已经在反哺主流:Go在1.14版本(2020年)加入了基于信号的异步抢占,解决的正是"无让出点的死循环饿死调度器"这同一个病。BEAM不是另类,它是把别人后来才补的课,1980年代就当默认值。
(视频原话大意)进程间不共享内存,那怎么交换数据?拷贝式消息传递:发送方把数据复制一份塞进接收方的信箱。听上去"浪费",但复制买来的是完全隔离——两边的堆彻底独立,谁也污染不了谁(大块二进制数据另有共享优化,此处不展开)。
更深远的是,本地进程和远程节点之间用的也是同一套消息语义。代码不用改,进程就能从"同一台机器"搬到"另一个机房",这就是位置透明:分布式在BEAM里不是外挂框架,而是语言内建的延伸。
(我的延伸)这条设计的当代回声,是微服务时代我们花大价钱买的东西:RPC框架、服务网格、序列化协议——本质上都是在给"本没有隔离语义的运行时"补拷贝边界。BEAM出生就有这个边界,代价是单次拷贝的开销,收益是无需框架的一致心智模型。快与慢的账,各人自己算,但它至少让你看清:微服务的复杂度里,有多少是运行时欠的债。
(视频原话大意)最后一块拼图是错误处理哲学。常规思路是防御式编程:预判每一种失败,写捕获、写重试、写降级,结果错误处理的代码往往比业务还多,还总有防不住的。Erlang的哲学反过来——let it crash:进程遇到搞不定的状态,别挣扎,直接崩。收拾残局是"别人"的事:监督者(supervisor)盯着下属进程,谁崩了就按预定策略重启它,重启后的进程回到干净的初始状态,带着坏的旧状态继续跑反而更危险。
由此整个系统形成一棵监督树:最核心的少量代码(error kernel)必须绝对正确,外层业务进程随便崩、随时修,系统的健壮性不来自每个零件永不损坏,而来自损坏可以被局限、被替换。
(我的延伸)把它翻译成通信人熟悉的语言:这就是网管系统的自动重启策略 + 进程级容错粒度。区别在于,网元的自愈是运维系统外挂的,而OTP把它做成了程序结构本身。看完这场演讲再看我们自己的网元告警日志,你会忍不住想:那些凌晨三点的"进程僵死",有多少是当年就该关进笼子里的。
flowchart TD
A["电信级可用性命题"] --> B["设计哲学:隔离"]
B --> C["进程沙箱:独立堆"]
B --> D["消息传值:拷贝通信"]
B --> E["抢占式调度"]
B --> F["监督树:放任崩溃"]
C --> C1["崩溃不传染"]
C --> C2["无锁免竞争"]
C --> C3["无全局GC暂停"]
D --> D1["位置透明:分布式免费"]
E --> E1["失控进程抢不走CPU"]
F --> F1["重启回到已知好状态"]
C1 --> G["可预测的低延迟"]
C3 --> G
E1 --> G
F1 --> H["九个9的可用性传说"]
看完这堂课,即使你一行Erlang都不会写,也有四件立刻能用上的事。
第一,把"隔离"当设计审查项。下次评审方案时加一个问题:这个模块崩了,会不会把别人带走?共享内存、全局单例、公共线程池,都是"崩溃传染"的高发区。在Go、Java里做不到BEAM式的绝对隔离,但"每个请求独立goroutine/任务 + 独立超时 + panic recover边界"这类廉价隔离,值得成为默认习惯。
第二,盯住你的GC暂停。长尾延迟的元凶常常不是慢代码,而是全局暂停。给延迟敏感的服务建立GC指标监控(暂停次数、最大暂停时长),比单纯堆机器更有效。BEAM给的启发是结构性的:能用"短命对象 + 小堆"就别造"长寿大对象",让垃圾生得快、死得早。
第三,区分吞吐型任务和延迟型任务。把发疯的批处理和在线请求塞进同一个调度域,等于让一辆失控卡车和救护车抢车道。物理隔离(独立实例、独立队列、独立线程池)永远优于事后调参。
第四,重新理解"重启"。别把重启当耻辱,把它当设计:确保每个服务都能在崩溃后回到干净初始状态,配上合理的重启策略和告警,比防御式地写一万行catch更接近可靠。OTP用了四十年证明:可靠性不是不犯错,而是每个错都有人接。
BEAM用四十年证明:可靠性不是靠人人完美,而是靠给每个崩溃准备一个笼子——隔离做得越彻底,系统越敢出事。
本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。 ```
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
