Elixir 并发容错语言
FreeGuideOnline
18阅读
2026-07-13
elixir iex> IO.puts("Hello, Elixir!") Hello, Elixir! :ok
## Elixir 快速入门
### 核心数据类型
```elixir
iex> 42 # 整数
iex> 3.14 # 浮点数
iex> "hello" # 字符串(UTF-8二进制)
iex> :atom # 原子(类似符号常量)
iex> [1, 2, 3] # 列表(链表)
iex> {1, :ok, "result"} # 元组
iex> %{:a => 1, :b => 2} # 映射(Map)
模式匹配
Elixir 中 = 不是赋值,而是匹配。
iex> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}
iex> a
:hello
iex> c
42
# 列表也可以匹配
iex> [head | tail] = [1, 2, 3]
[1, 2, 3]
iex> head
1
iex> tail
[2, 3]
模式匹配被广泛用于函数定义、控制流等,让代码异常清晰。
函数与管道
定义有名函数:
defmodule Math do
def sum(a, b) do
a + b
end
end
匿名函数:
add = fn a, b -> a + b end
add.(1, 2) # 调用时有个点
管道操作符 |> 将前一个表达式的结果作为第一个参数传递给下一个函数,使数据转换链一目了然:
"hello world"
|> String.upcase() # "HELLO WORLD"
|> String.split() # ["HELLO", "WORLD"]
|> Enum.join("-") # "HELLO-WORLD"
并发基石:Elixir 进程
创建进程与消息传递
Elixir 的并发模型基于 Actor 模型。每个进程独立运行,通过消息传递通信,不共享内存。
使用 spawn 创建一个新进程:
pid = spawn(fn ->
receive do
{:hello, msg} -> IO.puts("Received: #{msg}")
end
end)
# 向进程发送消息
send(pid, {:hello, "World"})
进程会一直循环接收消息,除非结束。更常见的方式是使用递归保持进程活跃:
defmodule Counter do
def start do
spawn(fn -> loop(0) end)
end
defp loop(count) do
receive do
:increment -> loop(count + 1)
{:get, caller} ->
send(caller, {:count, count})
loop(count)
end
end
end
counter = Counter.start()
send(counter, :increment)
send(counter, {:get, self()}) # self() 返回当前进程PID
receive do
{:count, value} -> IO.puts("Count is #{value}")
end
进程隔离与错误隔离
进程之间的崩溃不会互相影响。如果一个进程崩溃,只有它自己会终止,其他进程毫不知情。这是实现容错的基础。
# 一个会崩溃的进程
spawn(fn -> raise "oops" end)
# 当前进程照常运行
IO.puts("I'm still alive")
构建容错系统:OTP 监督树
实际开发中,我们不会从原始 spawn 和 receive 开始,而是使用 OTP 提供的行为(Behaviour),如 GenServer 和 Supervisor。
GenServer:通用服务器模式
GenServer 封装了客户端 - 服务器交互模式,包括处理同步/异步调用、状态管理、代码热升级等。
defmodule Stack do
use GenServer
# 客户端 API
def start_link(initial_stack) do
GenServer.start_link(__MODULE__, initial_stack, name: __MODULE__)
end
def push(element) do
GenServer.cast(__MODULE__, {:push, element})
end
def pop do
GenServer.call(__MODULE__, :pop)
end
# 服务端回调
@impl true
def init(stack) do
{:ok, stack}
end
@impl true
def handle_cast({:push, element}, stack) do
{:noreply, [element | stack]}
end
@impl true
def handle_call(:pop, _from, [head | tail]) do
{:reply, head, tail}
end
end
cast 是异步,call 是同步。
Supervisor:监督者
Supervisor 负责启动、停止、监控子进程,并定义崩溃时的重启策略。
defmodule Stack.App do
use Application
def start(_type, _args) do
children = [
{Stack, []} # 子进程规格,启动 Stack 进程
]
opts = [strategy: :one_for_one, name: Stack.Supervisor]
Supervisor.start_link(children, opts)
end
end
- 策略
:one_for_one:当一个子进程挂掉,仅重启该进程。 - 其他策略如
:one_for_all、:rest_for_one可以应对不同的依赖关系。
启动 Application 后,如果 Stack 进程因异常崩溃,Supervisor 会自动以初始状态重启它,外部客户端感觉不到中断(除非在崩溃瞬间有请求被丢弃,但系统自我修复)。
Elixir 的并发工具箱
Task:并发执行单元
Task 提供了一种便捷的方式,用于执行一次性计算并获取结果。
task = Task.async(fn -> heavy_computation() end)
# ... 做其他事情
result = Task.await(task) # 等待并获取结果
Agent:简单的状态抽象
Agent 是 GenServer 的简化版,仅用于状态管理:
{:ok, agent} = Agent.start_link(fn -> 0 end)
Agent.update(agent, fn count -> count + 1 end)
Agent.get(agent, fn count -> count end) # 1