强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Erlang/OTP 完全指南 / 01 - Erlang 简介

第 01 章:Erlang 简介

“Erlang 不是一门语言,它是一种构建可靠系统的方式。” — Joe Armstrong


1.1 历史背景

1.1.1 诞生故事

Erlang 诞生于 1986 年,由瑞典爱立信(Ericsson)公司的 Joe Armstrong、Robert Virding 和 Mike Williams 创造。

时间 事件
1986 Joe Armstrong 开始设计 Erlang
1987 首个原型实现完成
1991 Erlang 首次公开发布
1996 OTP(Open Telecom Platform)框架发布
1998 Erlang/OTP 开源
2006 Erlang 在并发编程领域重新受到关注
2008 WhatsApp、RabbitMQ 等项目兴起
2017 Erlang/OTP 20 引入 Maps 和新特性
2024+ Erlang/OTP 26+ 持续演进

1.1.2 设计目标

Erlang 最初为电信系统设计,核心要求:

  1. 高并发(Concurrency):同时处理大量呼叫
  2. 高可用(Availability):99.9999999%(九个九)的正常运行时间
  3. 软实时(Soft Real-time):低延迟响应
  4. 热代码升级(Hot Code Upgrade):不停机更新
  5. 容错(Fault Tolerance):局部故障不影响整体

1.1.3 “九个九"的含义

可用性等级 每年停机时间 典型场景
99% (两个九) 3.65 天 普通 Web 应用
99.9% (三个九) 8.76 小时 电商平台
99.99% (四个九) 52.6 分钟 金融系统
99.999% (五个九) 5.26 分钟 电信核心网
99.9999999% (九个九) 31 毫秒 爱立信 AXD301 ATM 交换机

1.2 Erlang 的特性

1.2.1 函数式编程

Erlang 是一门函数式编程(Functional Programming)语言:

%% 变量不可变(Single Assignment)
X = 1.
%% X = 2.  % 错误!变量已绑定

%% 函数是一等公民
Double = fun(X) -> X * 2 end.
Double(5).  %% => 10

%% 无副作用(理想情况)
%% 不修改输入数据,而是返回新的数据

1.2.2 轻量级进程

Erlang 的"进程"不是操作系统进程,也不是线程:

特性 OS 进程 OS 线程 Erlang 进程
内存占用 数 MB 数百 KB 约 2 KB
创建时间 数毫秒 数十微秒 约 1 微秒
数量上限 数千 数万 数百万
调度方式 OS 内核 OS 内核 BEAM 虚拟机
通信方式 IPC 共享内存 消息传递
%% 创建 100 万个进程
Pids = [spawn(fun() -> receive stop -> ok end end)
        || _ <- lists:seq(1, 1000000)].
length(Pids).  %% => 1000000

1.2.3 消息传递

进程间通信的唯一方式是消息传递(Message Passing):

%% 进程 A 发送消息给进程 B
Pid ! {hello, "world"}.

%% 进程 B 接收消息
receive
    {hello, Msg} -> io:format("收到: ~p~n", [Msg])
end.

1.2.4 容错设计

Erlang 的容错基于 “Let it crash” 哲学:

%% 不要这样写(防御性编程)
handle(Data) ->
    if
        is_list(Data) -> process_list(Data);
        is_tuple(Data) -> process_tuple(Data);
        true -> error  %% 覆盖所有情况?
    end.

%% Erlang 方式(Let it crash)
handle(Data) ->
    process(Data).  %% 让它崩溃,由 Supervisor 处理

1.3 OTP 框架

1.3.1 什么是 OTP?

OTP(Open Telecom Platform)是一个用于构建并发、容错应用程序的框架和库集合:

┌─────────────────────────────────────────┐
│              你的应用程序                  │
├─────────────────────────────────────────┤
│  GenServer  GenStateMachine  GenEvent    │  ← 行为模式(Behaviours)
├─────────────────────────────────────────┤
│  Supervisor         Application          │  ← 监督树
├─────────────────────────────────────────┤
│  ETS    Mnesia    Logger    Crypto       │  ← 标准库
├─────────────────────────────────────────┤
│              BEAM 虚拟机                  │
└─────────────────────────────────────────┘

1.3.2 OTP 三大支柱

组件 英文名 作用
通用服务器 GenServer 封装有状态的并发服务
监督者 Supervisor 监控子进程,崩溃时自动重启
应用 Application 组织和管理应用生命周期
%% 一个简单的 OTP 应用结构
%% 应用(Application)
%%   └── 监督者(Supervisor)
%%         ├── 服务器(GenServer)1
%%         ├── 服务器(GenServer)2
%%         └── 服务器(GenServer)3

1.3.3 监督树示例

          [Application]
               │
          [Supervisor]
          /    |    \
   [GS1]   [GS2]   [GS3]
               │
          [Supervisor2]
          /          \
     [Worker1]    [Worker2]

1.4 适用场景

1.4.1 电信领域

Erlang 的"老家”:

  • 交换机控制:爱立信 AXD301 ATM 交换机
  • 信令网关:SS7/SIP 协议处理
  • 移动核心网:4G/5G 核心网元

1.4.2 消息队列

项目 描述 使用 Erlang 的部分
RabbitMQ 最流行的开源消息代理 核心 broker 全部用 Erlang
EMQX 大规模 MQTT 消息服务器 核心引擎
VerneMQ 分布式 MQTT broker 全部用 Erlang

1.4.3 即时通讯

项目 峰值规模
WhatsApp 9 亿用户,50 名工程师
微信 部分后端服务使用 Erlang

1.4.4 数据库与存储

项目 描述
CouchDB 文档数据库,HTTP REST API
Riak 分布式 KV 数据库
Mnesia Erlang 内置分布式数据库

1.4.5 实时系统

  • 游戏服务器:高并发玩家连接
  • 物联网(IoT):海量设备连接管理
  • 金融交易:低延迟订单处理
  • CDN 边缘计算:内容分发节点

1.4.6 何时不适合使用 Erlang?

场景 原因 替代方案
CPU 密集计算 BEAM 调度器开销 C/C++/Rust
科学计算 缺乏数值库 Python/Julia
桌面 GUI 无成熟 GUI 框架 C#/Swift
移动端开发 不支持 Kotlin/Swift
简单 CRUD Web 生态不如主流语言 Go/Node.js

1.5 BEAM 虚拟机

1.5.1 架构概览

源代码 (.erl)
    ↓
编译器 (erlc)
    ↓
字节码 (.beam)
    ↓
BEAM 虚拟机 (erl)
    ↓
操作系统

1.5.2 BEAM vs 其他虚拟机

特性 BEAM JVM .NET CLR
并发模型 Actor 线程 Task/TPL
进程开销 ~2KB ~1MB 栈 ~1MB
GC 策略 每进程独立 GC 全局 GC 分代 GC
热更新 原生支持 需要框架 需要框架
调度 抢占式 抢占式 抢占式
延迟保证 软实时 一般无 一般无

1.5.3 Erlang 运行时系统

%% 查看运行时信息
erlang:system_info(otp_release).    %% OTP 版本
erlang:system_info(schedulers).     %% 调度器数量(通常等于 CPU 核心数)
erlang:system_info(process_count).  %% 当前进程数
erlang:memory().                    %% 内存使用情况

1.6 Erlang 生态系统

1.6.1 核心工具

工具 用途
erlc Erlang 编译器
erl 交互式 Shell(REPL)
rebar3 构建工具(类似 Maven/npm)
observer 可视化监控工具
dialyzer 静态类型分析

1.6.2 知名 Erlang 项目

WhatsApp      ── 即时通讯(9 亿用户)
RabbitMQ      ── 消息队列(最流行 AMQP broker)
CouchDB       ── 文档数据库
Riak          ── 分布式 KV 存储
EMQX          ── MQTT broker(百万连接)
Discord       ── 部分后端服务
Klarna        ── 支付系统
Ericsson      ── 电信设备
Nintendo      ── 部分在线服务

1.7 快速上手预览

1.7.1 Hello World

%% hello.erl
-module(hello).
-export([world/0]).

world() ->
    io:format("Hello, Erlang!~n").

1.7.2 简单并发

%% 创建一个打印消息的进程
Pid = spawn(fun() ->
    receive
        {greet, Name} ->
            io:format("Hello, ~s!~n", [Name])
    end
end).

Pid ! {greet, "Erlang"}.

1.7.3 简单监督树

%% Supervisor 启动两个 worker
init(_) ->
    Children = [
        #{id => worker1, start => {my_worker, start_link, []}},
        #{id => worker2, start => {my_worker, start_link, []}}
    ],
    {ok, {#{strategy => one_for_one, intensity => 5, period => 10}, Children}}.

1.8 Erlang vs 其他语言

维度 Erlang Go Java Elixir
类型 动态强类型 静态强类型 静态强类型 动态强类型
并发模型 Actor CSP (goroutine) 线程/虚拟线程 Actor (运行在 BEAM 上)
容错 原生监督树 手动处理 需要框架 原生监督树
热更新 原生支持 不支持 需要框架 原生支持
学习曲线 中等 低 中等 中等
生态规模 小 大 极大 中等
运行平台 BEAM 原生 JVM BEAM

1.9 注意事项

⚠️ 常见误区

  1. Erlang 不是银弹:不适合 CPU 密集型任务,数值计算性能远低于 C/Rust
  2. 动态类型的代价:Dialyzer 可以做静态分析,但无法替代编译期类型检查
  3. 生态较小:第三方库数量远少于 Java/Python/Go,需要自己造轮子
  4. 学习曲线:函数式思维 + OTP 设计模式需要时间适应
  5. 调试困难:并发 bug 难以复现,需要依赖 tracing 工具

💡 为什么学 Erlang?

  • 理解 Actor 模型和并发编程的最佳范本
  • OTP 的设计模式可以迁移到其他语言(Akka、Vert.x 等)
  • Elixir 运行在 BEAM 上,学会 Erlang 更容易上手 Elixir
  • 电信、消息队列、IoT 等领域仍有大量 Erlang 岗位

1.10 扩展阅读


下一章:02 - 环境搭建