5Seven Languages in Seven Weeks

Erlang

并发编程

进程模型消息传递Let it crashOTP

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第5章:Erlang(并发编程)

一、导读

1.1 本章学习目标

第5章介绍 Erlang——一门为并发和容错而生的语言。Erlang 由爱立信开发,用于构建电信系统,其核心理念是"Let it crash"——不试图预防错误,而是让系统在错误后自动恢复。

本章的核心学习目标包括:

  • 理解 Erlang 的进程模型:轻量级进程、消息传递、无共享
  • 掌握"Let it crash"哲学
  • 理解 OTP(Open Telecom Platform)框架
  • 体验函数式编程在并发中的优势

1.2 为什么本章如此重要

Erlang 的并发模型影响了 Go、Elixir、Akka 等众多现代语言/框架。在多核时代,Erlang 的"每个进程独立、通过消息通信"模型被证明是构建高并发、高可用系统的最佳实践之一。WhatsApp 用 Erlang 支撑了数亿用户的同时在线。

1.3 核心问题引导

Erlang 的进程和操作系统线程有什么本质区别?

"Let it crash"为什么能构建更可靠的系统?

监督树(Supervision Tree)是如何工作的?

消息传递为什么比共享内存更安全?


二、核心概念详解

2.1 进程与消息传递

erlang
% 创建进程
Pid = spawn(fun() ->
    receive
        {From, Message} ->
            From ! {self(), "Hello, " ++ Message}
    end
end),

% 发送消息
Pid ! {self(), "World"},

% 接收回复
receive
    {Pid, Response} -> io:format("~s~n", [Response])
end.

2.2 Let it crash

erlang
% 不试图预防错误,而是让监督者重启
-module(worker).
-export([start/0, loop/0]).

start() -> spawn(fun loop/0).

loop() ->
    receive
        {do_work, Data} ->
            % 如果这里崩溃,监督者会重启
            Result = process(Data),
            io:format("Result: ~p~n", [Result]),
            loop();
        stop ->
            ok
    end.

process(Data) ->
    % 可能崩溃的操作
    1 / Data.

2.3 监督树

erlang
-module(my_supervisor).
-behaviour(supervisor).

init(_) ->
    {ok, {
        {one_for_one,  % 重启策略
         5,            % 最大重启次数
         60},          % 时间窗口(秒)
        [
            {worker1, {worker, start, []}, 
             permanent, 5000, worker, [worker]}
        ]
    }}.

2.4 模式匹配与递归

erlang
% 列表操作
sum([]) -> 0;
sum([H|T]) -> H + sum(T).

% 快速排序
qsort([]) -> [];
qsort([Pivot|Rest]) ->
    {Smaller, Larger} = partition(Pivot, Rest, [], []),
    qsort(Smaller) ++ [Pivot] ++ qsort(Larger).

partition(_, [], Smaller, Larger) -> {Smaller, Larger};
partition(Pivot, [H|T], Smaller, Larger) ->
    case H =< Pivot of
        true -> partition(Pivot, T, [H|Smaller], Larger);
        false -> partition(Pivot, T, Smaller, [H|Larger])
    end.

三、实战案例

3.1 案例一:简单的聊天服务器

erlang
-module(chat_server).
-export([start/0, loop/1]).

start() ->
    register(chat, spawn(fun() -> loop([]) end)).

loop(Clients) ->
    receive
        {join, Pid} ->
            io:format("Client joined: ~p~n", [Pid]),
            loop([Pid | Clients]);
        {leave, Pid} ->
            io:format("Client left: ~p~n", [Pid]),
            loop(Clients -- [Pid]);
        {broadcast, From, Message} ->
            lists:foreach(
                fun(Pid) -> 
                    case Pid =:= From of
                        true -> ok;
                        false -> Pid ! {message, From, Message}
                    end
                end,
                Clients
            ),
            loop(Clients)
    end.

四、本章小结

进程模型:轻量级进程、消息传递、无共享状态。

Let it crash:不预防错误,让监督者重启崩溃的进程。

监督树:层次化的进程管理,自动故障恢复。

函数式编程:不可变数据让并发变得简单。


五、思考与练习

Erlang 的消息传递和共享内存并发各有什么优缺点?

"Let it crash"在什么场景下适用?什么场景下不适用?

用 Erlang 的监督树模型设计一个高可用的 Web 服务。


5/7Erla
0%