模块 06查看课程目录
06
OTP中级ElixirErlangOTP
从手写循环推导 OTP
GenServer 不是“放状态的盒子”,而是一套经过约束的协议。
5检查点
7 小时预计完成
为什么先学这个
先修正心智模型,再增加 API
OTP behaviour 把经过反复验证的消息循环、系统消息、调试与升级钩子统一起来。它减少样板,但不会替你决定 API、状态归属、背压和超时策略。
学完你能做到
可验证的学习目标
- 能区分客户端 API 与 callback 实现
- 能为一致性要求选择 call 或 cast
- 能把长期进程正确放进监督树
前置知识
- 完成裸进程章节
- 写过 request/reply 协议
最小心智模型
先抓住三个词
behaviour
一组 callback 合约。实现模块遵守合约,通用运行框架负责循环、系统消息与调试。
call
同步请求。调用者等待 reply,天然形成等待,但并不等同于完整背压。
cast
异步消息。调用者不知道是否已处理,过量 cast 可能让 mailbox 无界增长。
双语代码桥
先对齐协议,再看标点
客户端函数隐藏消息形状;callback 只处理协议。两边的返回 tuple 语义一一对应。
Elixir
defmodule KV do
use GenServer
def start_link(opts), do: GenServer.start_link(__MODULE__, %{}, opts)
def get(server, key), do: GenServer.call(server, {:get, key})
def put(server, key, value), do: GenServer.call(server, {:put, key, value})
@impl true
def init(state), do: {:ok, state}
@impl true
def handle_call({:get, key}, _from, state),
do: {:reply, Map.fetch(state, key), state}
def handle_call({:put, key, value}, _from, state),
do: {:reply, :ok, Map.put(state, key, value)}
endErlang
-module(kv).
-behaviour(gen_server).
-export([start_link/0, get/2, put/3]).
-export([init/1, handle_call/3]).
start_link() -> gen_server:start_link(?MODULE, #{}, []).
get(Server, Key) -> gen_server:call(Server, {get, Key}).
put(Server, Key, Value) ->
gen_server:call(Server, {put, Key, Value}).
init(State) -> {ok, State}.
handle_call({get, Key}, _From, State) ->
{reply, maps:find(Key, State), State};
handle_call({put, Key, Value}, _From, State) ->
{reply, ok, State#{Key => Value}, State}.LAB
约 15–25 分钟可运行实验
cast 为什么不是免费吞吐
把 put 改为 cast,然后快速发送十万条更新。观察 mailbox 与响应性,而不是只看发送循环有多快。
- 01
暴露 `put_async/3` 使用 cast
- 02
批量发送大量更新
- 03
并行调用一个同步 get,记录延迟和 message_queue_len
在终端 / shell 中运行
:erlang.process_info(pid, :message_queue_len)预期观察
- 发送者很快结束不代表服务端已完成
- 同步 get 可能排在大量 cast 后面
故意弄坏
在 handle_cast 中加入慢 I/O,继续提升发送速率。观察 mailbox 是否持续增长。
这个实验能证明
异步 API 会把等待从调用者转移到服务端队列。
这个实验不能证明
单机压测不能给出生产安全阈值;调度器、消息大小和外部 I/O 都会改变结果。
快速自测
下面哪个理由最适合使用 cast?
本章挑战
双语限流任务队列
任选一种语言写公开 API,另一种语言写 worker。最多并发 N 个任务,超出部分进入有界队列,满时返回 `busy`。
提示 1轻推一下
长期状态由一个进程拥有,但任务本身用独立进程执行。
提示 2缩小问题
用 monitor 回收 worker 完成与崩溃。
提示 3接近实现
不要让 API 静默接受无限任务。
提示 4用验收标准反推
从下面每一条验收标准倒推一个最小测试。若某条无法写成测试,先把表述改成可观察结果。
完成标准
- 并发数与队列长度都有上限
- worker 崩溃不会丢失容量计数
- 拒绝策略对调用者可见
带走这三句话
复习卡
- 1behaviour 固定循环合约,不替代业务协议设计。
- 2客户端 API 应隐藏消息格式。
- 3cast 把压力移进 mailbox;过载策略仍需显式设计。
继续核对