分布式Erlang:网络分区恢复及分布式应用中heart使用问题
我在生产环境里也碰到过类似的Erlang主备集群网络分区问题,先从你看到的现象核心原因说起,再拆解恢复机制和Heart的正确用法:
首先,你看到的removing (timedout) connection错误是Erlang分布式节点的默认行为——节点之间依赖net_ticktime参数(默认60秒)维持连接心跳。当网络断开超过这个时间,节点会判定对方已死亡,所以备节点触发故障转移启动服务,这完全符合预期。
一、网络分区后的恢复机制
网络恢复后,Erlang节点会自动重建连接,但这时候会出现脑裂问题:原主节点和备节点同时在运行服务,违背了主备设计的初衷。你可以通过以下几种方式处理:
利用全局名称注册冲突处理
使用Erlang内置的global模块注册唯一的服务标识(比如global:register_name(main_service, self()))。当网络恢复后,如果两个节点都注册了同一个名称,global会触发冲突回调。你可以在回调里实现逻辑:比如让备节点主动停止服务(因为原主节点通常持有更完整的业务状态),或者根据节点启动时间、优先级决定谁接管。基于Mnesia的分布式数据同步
如果业务依赖Mnesia存储状态,可将表设置为disc_copies或ram_copies分布式模式。网络恢复后,Mnesia会自动同步节点间的数据,你还能配置冲突解决策略(比如last_write_wins)来处理同步时的数据不一致问题,确保状态一致性。自定义应用层心跳检测
除了Erlang底层的net_ticktime,在应用层实现自己的心跳机制:比如主备节点每隔N秒互相发送包含业务状态的心跳包。这样不仅能检测网络连通性,还能验证对方服务是否正常运行,避免单纯依赖网络连接误判节点状态。主动触发回切逻辑
原主节点恢复后,让它主动向备节点发送接管通知。备节点收到通知后,优雅关闭自身服务(比如处理完当前请求、持久化关键状态),然后退出服务模式回到备用状态。
二、Heart组件的正确使用方式
很多人会误解Heart的用途——它的核心作用是监控Erlang虚拟机(VM)的健康状态,而非解决网络分区问题。正确用法如下:
启用Heart
在启动Erlang节点时添加-heart参数,示例:erl -heart -name main@192.168.1.100 -setcookie my_cluster_cookie这会启动一个独立的heart进程,定期向VM发送心跳信号,如果VM无响应,heart会自动重启VM。
调整Heart超时
默认超时时间是60秒,你可以通过代码动态调整:heart:set_timeout(30000). % 设置为30秒这个超时是heart检测本地VM存活的间隔,和
net_ticktime是完全独立的两个参数。配合系统级监控
Heart最好和系统进程管理工具(比如systemd、init.d)配合使用。比如在systemd服务文件中设置Restart=always,这样即使heart重启了VM,系统也会确保节点进程持续运行,避免单点故障。Heart的局限性
注意:Heart不能直接解决网络分区问题,它只负责监控本地VM是否正常。要处理网络分区,你还是需要结合前面提到的集群管理逻辑。另外,Heart依赖系统的kill命令,要确保Erlang进程有足够的权限执行该操作。
额外实践建议
- 调整
net_ticktime:如果觉得60秒的断开检测太长,可以在启动节点时设置更小的值(比如-net_ticktime 30),但不要设置过小,否则会增加网络开销。 - 脑裂检测:如果集群规模更大,可实现投票机制(比如奇数个节点),当网络分区时,节点通过投票选出主节点,避免多主情况。
- 优雅关闭:备节点停止服务时,一定要处理完当前请求、持久化关键数据,避免数据丢失或业务中断。
内容的提问来源于stack exchange,提问作者Roman Rabinovich

