一、Erlang分布式系统的故障特点
Erlang作为一门专为分布式和高并发设计的语言,天生适合构建容错系统。但分布式环境下,网络分区、节点宕机、消息丢失等问题依然常见。比如下面这个典型场景:
% 技术栈:Erlang/OTP 25
% 场景:节点A向节点B发送消息,但B可能因网络问题未收到
send_msg(TargetNode, Msg) ->
case net_adm:ping(TargetNode) of % 检查节点连通性
pong ->
{ok, _} = gen_server:call({server, TargetNode}, Msg), % RPC调用
io:format("Message sent successfully~n");
pang ->
io:format("Node ~p unreachable~n", [TargetNode]),
{error, node_down} % 处理节点不可达
end.
注释:
net_adm:ping/1是Erlang自带的节点探测函数gen_server:call/2可能因超时失败(默认5秒)
这类问题往往表现为:消息延迟、状态不一致或进程孤立。
二、核心解决策略
2.1 超时与重试机制
Erlang的gen_server:call默认超时为5秒,但分布式场景需要更灵活的控制:
% 自定义超时和重试
retry_call(Node, Msg, Retries) when Retries > 0 ->
case gen_server:call({server, Node}, Msg, 3000) of % 3秒超时
{error, timeout} ->
timer:sleep(1000),
retry_call(Node, Msg, Retries - 1); % 指数退避更佳
Reply ->
Reply
end;
retry_call(_, _, 0) -> {error, max_retries}.
2.2 进程监控与接管
通过link和monitor实现进程级容错:
start_worker() ->
Pid = spawn_link(fun worker_loop/0), % 建立双向链接
erlang:monitor(process, Pid), % 额外监控
{ok, Pid}.
worker_loop() ->
receive
{work, Data} -> process_data(Data);
shutdown -> exit(normal)
after 5000 -> exit(no_work) % 5秒无消息自毁
end.
关键点:
spawn_link在子进程崩溃时通知父进程monitor提供更详细的退出原因(通过DOWN消息)
三、实战:分布式数据库同步
假设我们实现一个多节点的KV存储:
% 技术栈:Mnesia + Erlang
init_db(Nodes) ->
mnesia:create_schema(Nodes), % 初始化集群
mnesia:create_table(user, [
{disc_copies, Nodes}, % 数据持久化节点
{attributes, [id, name]} % 表结构
]).
write_data(Key, Value) ->
Trans = fun() ->
case mnesia:write({user, Key, Value}) of
ok -> ok;
{aborted, Reason} ->
% 自动切换到其他节点写入
mnesia:activity(transaction, mnesia:write, [{user, Key, Value}])
end
end,
mnesia:sync_transaction(Trans). % 同步事务
注意:
- Mnesia在节点失效时会自动重定向操作
sync_transaction比异步更安全但性能较低
四、高级技巧与陷阱规避
4.1 脑裂处理
网络分区可能导致"双主"问题。解决方案:
% 使用Erlang的全局锁(global模块)
acquire_lock(Resource) ->
case global:set_lock({Resource, self()}) of
true ->
{ok, LockId};
false ->
{error, conflict}
after 10000 -> % 10秒后强制释放
global:del_lock({Resource, self()})
end.
4.2 消息堆积防护
使用erlang:system_info(message_queue_len)监控:
check_mailbox() ->
case erlang:process_info(self(), message_queue_len) of
{_, Len} when Len > 1000 ->
% 触发背压机制或告警
{warning, mailbox_full};
_ ->
ok
end.
五、总结与选型建议
适用场景:
- 电信级可靠性要求的系统
- 需要软实时响应的场景(如游戏服务器)
优缺点:
- ✅ 轻量级进程模型支持百万级并发
- ❌ 学习曲线陡峭,生态不如Java/Python丰富
最后建议:
- 始终用
-export([init/1])显式定义函数可见性 - 在
sys.config中配置kernel参数调节分布式行为 - 使用
recon或observer_cli进行运行时诊断
评论