第 07 站展开探险地图
BEAM 节点失联
节点连上只是故事的开始;断线、迟到和旧数据也要有安排。
远程 worker 在请求执行中途断线时,调用者得到什么结果,系统怎样标记 stale、重试并避免重复执行?
节点已经失联,仪表盘仍显示绿色
远程节点在请求中途断开,最后一次指标仍被当作健康数据展示。调用者重试后,原任务又可能已经完成,外部动作因此执行两遍。
- nodeup、nodedown 与请求 reference 的时间线
- 最后一次样本的时间戳与 stale 标记
- 重试、幂等记录,以及重连后的重新注册日志
它要解决什么
分布式 Erlang 让远程发送写起来很自然,网络却仍会延迟或断开。节点名和 cookie 只帮助连接;一致性、投递、容量和安全仍要设计。
你会做到
能启动两个有名字的 BEAM 节点,让它们互相找到并发送消息
能说清
nodedown表示什么,也能分清远程 PID 与只在某个节点有效的注册名能用带上下文的日志、Telemetry 指标和 Observer 追踪 mailbox 与延迟
- 知道进程怎样通信、监督树怎样恢复,以及背压怎样表示忙不过来
- 能够启动一个基本 OTP Application;还没做过 release 也可以继续
先看做什么,再看怎么写
节点连断会变成消息。收到 nodedown 后如何降级或拒绝,要提前约好。
# 让当前进程接收节点连上与断开的消息
:net_kernel.monitor_nodes(true)
# 节点事件会像普通 BEAM 消息一样到达
receive do
{:nodeup, node} ->
# 记录刚刚连上的节点
Logger.info("node connected", node: node)
{:nodedown, node} ->
# 断连后由应用决定降级或重试
Logger.warning("node disconnected", node: node)
end为什么这样写
为什么节点能互发消息,还要写断线协议?
相近的发送写法只解决连接后的表达。网络仍会延迟、分区,旧数据也会假装新鲜。
重试提高成功机会,也会重复副作用;需要 stale、幂等和次数上限。
从 Java、Python、JavaScript 看过来
Java
- 熟悉的起点
- RPC、消息代理或集群框架。
- BEAM 在意什么
- 远程 PID 发送写起来接近本地发送,但失败语义仍是网络语义。
- 别带错直觉
- 方便的调用形式不提供恰好一次。
Python
- 熟悉的起点
- HTTP/RPC、Celery 等任务队列。
- BEAM 在意什么
- 节点事件也以 BEAM 消息进入应用。
- 别带错直觉
- 看到
nodedown不等于知道断线原因。
JavaScript
- 熟悉的起点
- fetch、WebSocket 或队列消费者。
- BEAM 在意什么
- 同一 VM 模型能延伸到节点间,但边界仍要有 timeout 与幂等。
- 别带错直觉
- 网络分区不会因为语法短而消失。
让一个节点离线
启动两个本地节点,连通后停掉一个。观察事件、远程进程和未完成请求。
- 01
使用相同 cookie 启动
a@127.0.0.1和b@127.0.0.1 - 02
从 A
pingB,并打开monitor_nodes - 03
停止 B,记录 A 收到的事件,也看看未完成请求怎样结束
# 尝试连接节点 b;成功返回 :pong,失败返回 :pang
Node.ping(:"b@127.0.0.1")- 连接成功时,
Node.ping/1返回:pong - B 停止后,A 会收到
nodedown - BEAM 会报告断连,但应用仍要自己决定请求是失败、稍后重试还是换一条路
给节点设置不同 cookie。确认失败发生在连接阶段,而非业务 handler。
节点的连通和断开可以被观察,并转成应用能够处理的事件。
本机停节点不能模拟丢包、长延迟、半开连接和脑裂。
这段代码里的关键词
node
参与分布式通信的 BEAM 实例,名字通常是 name@host。远程 PID 也含节点身份。
cookie
节点建立连接时使用的共享秘密。它不是完整安全方案,不能代替加密、隔离和访问控制。
Telemetry
Elixir 常用的事件测量工具。业务代码发事件,handler 负责统计或导出。
这里用了哪些设计模式
Circuit Breaker
节点不可用时停止继续路由,并用探测决定何时恢复。
Lease
让健康与注册信息按时间失效,不把旧数据当作当前事实。
Idempotent Consumer
让重试或重复投递不会重复产生外部效果。
分布式 Erlang 让远程 PID 收消息写起来像本地发送,它自动提供了什么?
画一张节点状态图
定期上报 scheduler、内存和关键 mailbox。断连后保留最后值,并标记 stale。
提示 1先迈一步
每个指标都带上采样时间,数字离开时间就容易骗人。
提示 2再缩小一点
断开后保留最后一次真实数据,同时明确标出 stale。
提示 3离答案很近了
观察 mailbox 连续变长的趋势,不要只盯着某一个瞬间的数字。
提示 4从终点往回想
先挑一条“过关信号”,为它写一个最小测试。如果电脑看不出结果,就把这句话改成一个真正能观察到的现象。
节点断开时不会显示成“所有指标都是健康的 0”
日志能看出 node、process 和 request/reference 属于谁
节点重连后能恢复上报,也不会把同一服务重复注册
记住三句话
- 1
远程发送写起来像本地发送,只是方便了表达,并没有让网络消失。
- 2
断连、timeout 和 stale 数据都要写进双方约定,而不是等出事再猜。
- 3
除了“进程还活着”,mailbox、延迟、重启次数和拒绝率更能说明系统是否真的健康。