You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单客户端异常致Chumak崩溃断连所有客户端的问题咨询

问题分析与解决方案

从你提供的错误日志来看,服务器崩溃的直接原因是:

** Reason for termination == **
{{noproc,{gen_server,call,[<0.10180.501>,incomming_queue_out]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,206}]},
{chumak_router,recv_message,2,
[{file,"/opt/system/system_server/src/_build/default/lib/chumak/src/chumak_router.erl"},
{line,101}]}...]}

简单说,chumak的Router进程(从日志里的chumak_router可以看出,你提到的REP服务应该是基于Router模式实现的)尝试调用一个已经不存在的客户端队列进程(<0.10180.501>),抛出了noproc异常,但chumak代码没有捕获这个异常,导致整个Router gen_server直接终止,进而引发服务器重启、所有客户端断连。

这个问题完全可避免,本质是chumak库的健壮性不足,未处理客户端进程意外退出的场景。下面是几个可行的解决/缓解方案:


1. 修复chumak库的异常处理(最直接根治)

你需要修改chumak的chumak_router.erl代码,在调用gen_server:call时捕获noproc异常,并清理无效的客户端映射:

找到recv_message函数(对应日志第101行),原代码大概是这样:

recv_message(Identity, _State) ->
    gen_server:call(Identity, incomming_queue_out).

修改为带异常捕获的版本:

recv_message(Identity, State = #state{lb_state = LbsState}) ->
    try gen_server:call(Identity, incomming_queue_out) of
        Message -> {ok, Message, State}
    catch
        exit:{noproc, _} ->
            % 清理该客户端的身份与进程映射
            {NewLbs, _RemovedIdentity} = chumak_lbs:remove_peer(LbsState, Identity),
            NewState = State#state{lb_state = NewLbs},
            {error, client_disconnected, NewState}
    end.

接着修改调用recv_message的queue_ready函数(日志第91行),处理返回的错误情况,阻止异常向上传播:

queue_ready(Identity, _Message, State) ->
    case recv_message(Identity, State) of
        {ok, ReceivedMessage, NewState} ->
            % 保留原有的消息分发逻辑
            dispatch_message(ReceivedMessage, NewState);
        {error, client_disconnected, NewState} ->
            % 忽略无效客户端的消息,返回更新后的状态
            {noreply, NewState}
    end.

这样当客户端因网络问题进程退出后,Router会自动清理对应映射,不会因调用不存在的进程而崩溃。

2. 添加进程监控(提前预防无效调用)

在chumak的Socket或Router模块中,给每个客户端队列进程添加erlang:monitor(process, Pid)监控。当收到进程退出的{'DOWN'}消息时,立即清理该客户端的映射,从根源上避免noproc异常。

比如在chumak_router的add_peer函数里添加监控:

add_peer(Identity, Pid, State = #state{lb_state = LbsState}) ->
    % 监控客户端队列进程
    erlang:monitor(process, Pid),
    NewLbs = chumak_lbs:add_peer(LbsState, Identity, Pid),
    State#state{lb_state = NewLbs}.

然后在handle_info中处理DOWN消息:

handle_info({'DOWN', _MonitorRef, process, Pid, _Reason}, State = #state{lb_state = LbsState}) ->
    % 根据进程ID清理映射
    {NewLbs, _RemovedIdentity} = chumak_lbs:remove_peer_by_pid(LbsState, Pid),
    {noreply, State#state{lb_state = NewLbs}};
handle_info(OtherMsg, State) ->
    % 保留原有的其他消息处理逻辑
    {noreply, State}.

这个方案能提前清除无效的客户端进程记录,避免后续的无效调用。

3. 服务器进程容错包装(临时应急方案)

如果暂时无法修改chumak库,可以在你的Erlang服务代码中,给chumak的Router进程添加监督进程(Supervisor),设置one_for_one重启策略,并限制重启频率(比如{max_restart, 3}, {max_time, 60})。这样即使Router崩溃,监督进程会快速重启它,减少对客户端的影响。

注意:这只是临时的容错手段,不能解决根本问题,优先推荐修复chumak的异常处理。

4. 客户端侧辅助优化

虽然问题根源在服务器,但客户端可以优化减少触发概率:

  • 网络恢复后,先重新建立ZeroMQ连接,再发送请求,不要直接发重复请求;
  • 设计幂等请求,确保服务器收到重复请求也不会引发逻辑问题;
  • 给客户端发送操作设置超时,避免无意义的重复发送。

总结:这个问题不是chumak的固有缺陷,只是缺少对客户端进程意外退出场景的处理。通过修改chumak添加异常捕获和进程监控,就能彻底解决服务器崩溃的问题,避免单个客户端异常引发的DoS风险。

内容的提问来源于stack exchange,提问作者Ken - Enough about Monica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:46:04