远程gen_server进程启动后立即崩溃问题排查求助
我有一个基于gen_server行为的主控制器模块controller.erl,需要在4台不同PC上创建4个tower.erl进程并实现后续通信。
tower模块的init/1方法已被调用(能看到对应的打印输出),其中设置的定时器本应触发handle_info/2与控制器通信,但4个tower进程启动后立即崩溃,导致该逻辑从未执行。sys:trace无法工作,且tower的terminate/2函数未被调用,无法捕获崩溃原因,而控制器进程运行正常。
控制器模块(controller.erl)核心代码
start_controller() -> gen_server:start({global, ?MODULE}, ?MODULE, [], []). init(_) -> global:register_name(controller,self()), % Start additional servers on remote nodes Node1 = 'a@10.0.2.15', Node2 = 'b@10.0.2.15', Node3 = 'c@10.0.2.15', Node4 = 'd@10.0.2.15', BORDERS_1=[XMIN_1,XMAX_1,YMIN_1,YMAX_1] =[0, 400,0,400], BORDERS_2 = [XMIN_2,XMAX_2,YMIN_2,YMAX_2] =[401, 800,0,400], BORDERS_3 = [XMIN_3,XMAX_3,YMIN_3,YMAX_3] =[0, 400,401,800], BORDERS_4 = [XMIN_4,XMAX_4,YMIN_4,YMAX_4] =[401, 800,401,800], ETS_1 = ets:new(ets1,[set,named_table]), ETS_2 = ets:new(ets2,[set,named_table]), ETS_3 = ets:new(ets3,[set,named_table]), ETS_4 = ets:new(ets4,[set,named_table]), {ok,PID_1} = rpc:call(Node1, tower, start_tower, [BORDERS_1]), {ok,PID_2} = rpc:call(Node2, tower, start_tower, [BORDERS_2]), {ok,PID_3} = rpc:call(Node3, tower, start_tower, [BORDERS_3]), {ok,PID_4} = rpc:call(Node4, tower, start_tower, [BORDERS_4]), REF_1 = erlang:monitor(process,PID_1), REF_2 = erlang:monitor(process,PID_2), REF_3 = erlang:monitor(process,PID_3), REF_4 = erlang:monitor(process,PID_4), TimerInterval = 500, % milliseconds erlang:send_after(TimerInterval, self(), {send_to_graphics}), %{ok,State} where State consists of [PID_i,REF_i,ETS_i,XMIN_i,XMAX_i,YMIN_i,YMAX_i] State = [{PID_1,REF_1,ETS_1,XMIN_1,XMAX_1,YMIN_1,YMAX_1},{PID_2,REF_2,ETS_2,XMIN_2,XMAX_2,YMIN_2,YMAX_2},{PID_3,REF_3,ETS_3,XMIN_3,XMAX_3,YMIN_3,YMAX_3},{PID_4,REF_4,ETS_4,XMIN_4,XMAX_4,YMIN_4,YMAX_4}], {ok, State}.
Tower模块(tower.erl)核心代码
init(Borders) -> io:format("The borders for PID ~p are ~p ~n",[self(),Borders]), % {ok,FD} = file:open("tower_init.txt",[write]), % file:write(FD,"initiated a tower with an rpc call"), % file:close(FD), [Xmin, Xmax, Ymin, Ymax] = Borders, Table = ets:new(planes, [set,public, named_table]), StripBusy = 0, XStart = 100, XEnd = 300, YStart = 100, YEnd = 300, Strip = {XStart,XEnd,YStart,YEnd}, State = {Table,Strip,{Xmin,Xmax,Ymin,Ymax},StripBusy}, timer:send_interval(1000, {self(), send_to_controller}), %% Start the timer for plane creation {ok,State}. start_tower([Xmin, Xmax, Ymin, Ymax]) -> gen_server:start_link({global, ?MODULE}, ?MODULE, [Xmin, Xmax, Ymin, Ymax], []). handle_info({_PID, send_to_controller},State) -> io:format("Sending to main controller ~p ~n",[self()]), Controller_PID = global:whereis_name(controller), ETS_as_list = tab2list(planes), gen_server:cast(Controller_PID,{self(),ETS_as_list}), timer:send_interval(1000, {self(), send_to_controller}), {noreply,State};
可能的崩溃原因
1. 全局注册名冲突
tower模块的start_tower/1使用gen_server:start_link({global, ?MODULE}, ...)启动进程,这意味着每个tower进程都会尝试向分布式集群的全局注册中心注册名为tower的进程。而全局注册名在整个集群中唯一,只有第一个启动的tower进程能注册成功,后续节点上的tower进程会因为注册失败直接崩溃。
由于gen_server:start_link是关联式启动,注册失败会直接触发进程退出,且此时进程还未完全初始化完成,terminate/2函数不会被调用。
2. ETS命名表冲突
tower的init/1中创建了名为planes的ETS命名表(ets:new(planes, [set,public, named_table])),而每个Erlang节点上的命名表名称也是唯一的:
- 如果目标节点之前启动过
tower进程且未清理planes表,新进程创建同名表时会报错; - 即使是首次启动,若存在并发创建的情况(虽然这里是串行rpc调用,但不排除节点间时序问题),也可能导致创建失败。
ETS表创建失败会使init/1抛出异常,直接导致进程崩溃,同样不会触发terminate/2。
3. 分布式全局名称服务同步问题
虽然rpc:call返回了{ok,PID},但分布式集群的global模块可能未完成名称同步,tower进程启动后若在初始化阶段隐含依赖controller的全局名称(虽然当前代码中init/1未直接调用,但不排除其他隐藏逻辑),可能因无法找到控制器进程而崩溃。不过从现有代码看,该可能性低于前两者。
4. Timer调用的冗余问题
tower的handle_info/2中再次调用timer:send_interval/2,会导致不断创建新的定时器,最终可能引发资源耗尽,但这是进程运行后的问题,不会导致启动后立即崩溃,可排除。
内容的提问来源于stack exchange,提问作者user15846642

