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

Erlang频率分配计费服务实现:耗时计算与代码修正

Erlang 频率管理计费服务实现修正

原代码存在的核心问题

  • 计费逻辑缺失:注释掉了费率计算部分,无法生成正确的费用账单
  • 时间粒度过粗:使用秒级时间戳统计时长,短于1秒的使用场景会出现计费误差
  • 容错能力不足:DETS表未配置自动修复,服务异常崩溃后可能出现表损坏无法加载的问题
  • 配置硬编码:合法频率范围、费率、表名等参数散落在逻辑代码中,后续调整维护成本高
  • 错误提示模糊:原有错误码语义不明确,客户端无法区分是自身重复申请还是频率被其他用户占用
  • 状态定义无效:使用占位符other_data_you_need作为服务状态,未实际承载运行时配置

修正后完整实现代码

-module(b).
-compile([export_all, nowarn_export_all]).

% 宏定义配置项
-define(VALID_FREQS, lists:seq(1,10)).
-define(RATE_PER_SEC, 5).
-define(DETS_FILE, "example.dets").
-define(DETS_TABLE, example).

run() ->
    file:delete(?DETS_FILE),
    % 打开DETS表并开启自动修复
    case dets:info(?DETS_TABLE, owner) of
        undefined ->
            {ok, _} = dets:open_file(?DETS_TABLE, [{file, ?DETS_FILE}, {repair, true}]);
        _ ->
            skip
    end,
    spawn(fun() -> init() end),
    timer:sleep(100),
    Client = self(),

    % 第一轮基础功能测试
    ?DETS_TABLE ! {allocate, Client, aaa, 5},
    ?DETS_TABLE ! {allocate, Client, bbb, 5},
    ?DETS_TABLE ! {allocate, Client, aaa, 5},
    timer:sleep(1000),
    ?DETS_TABLE ! {release, Client, aaa, 5},
    ?DETS_TABLE ! {release, Client, aaa, 5},
    timer:sleep(100),
    io:format("第一轮测试返回结果:~p~n", [receive_server_msg([])]),

    % 第二轮服务重启持久化测试
    ?DETS_TABLE ! {allocate, Client, aaa, 5},
    timer:sleep(100),
    exit(whereis(?DETS_TABLE), kill),
    timer:sleep(50),
    spawn(fun() -> init() end),
    timer:sleep(100),
    ?DETS_TABLE ! {allocate, Client, bbb, 5},
    ?DETS_TABLE ! {allocate, Client, aaa, 5},
    timer:sleep(1000),
    ?DETS_TABLE ! {release, Client, aaa, 5},
    ?DETS_TABLE ! {release, Client, aaa, 5},
    timer:sleep(100),
    io:format("第二轮测试(服务重启后)返回结果:~p~n", [receive_server_msg([])]),
    ?DETS_TABLE ! {stop},

    dets:close(?DETS_TABLE).

receive_server_msg(List) ->
    receive
        Reply ->
            receive_server_msg([Reply|List])
        after 0 ->
            lists:reverse(List)
    end.

init() ->
    erlang:register(?DETS_TABLE, self()),
    % 初始化服务状态,存储运行时配置
    State = #{rate => ?RATE_PER_SEC, valid_freqs => ?VALID_FREQS},
    manager(State).

manager(State) ->
    receive
        {stop} ->
            ok;
        {allocate, Requestor, Name, Frequency} ->
            case check_allocate_request_and_modify_state(Name, Frequency, State) of
                {ok, State1} ->
                    Requestor ! {grant, Frequency},
                    manager(State1);
                {error, Reason} ->
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        {release, Requestor, Name, Frequency} ->
            case check_release_request_and_modify_state(Name, Frequency, State) of
                {ok, Cost, State1} ->
                    Requestor ! {bill, Cost},
                    manager(State1);
                {error, Reason} ->
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        UnknownMsg ->
            logger:error("收到未知消息: ~p", [UnknownMsg]),
            manager(State)
    end.

check_allocate_request_and_modify_state(Name, Frequency, State = #{valid_freqs := ValidFreqs}) ->
    case lists:member(Frequency, ValidFreqs) of
        true ->
            case dets:lookup(?DETS_TABLE, Frequency) of
                [] ->
                    % 记录毫秒级起始时间,提升计费精度
                    dets:insert(?DETS_TABLE, {Frequency, Name, erlang:system_time(millisecond)}),
                    {ok, State};
                [{_, Name, _}] ->
                    {error, already_using_by_self};
                _ ->
                    {error, occupied_by_other}
            end;
        false ->
            {error, invalid_frequency}
    end.

check_release_request_and_modify_state(Name, Frequency, State = #{rate := Rate}) ->
    case dets:lookup(?DETS_TABLE, Frequency) of
        [{_, Name, StartTimeMs}] ->
            dets:delete(?DETS_TABLE, Frequency),
            % 时长转秒后乘以费率,保留2位小数
            DurationSec = (erlang:system_time(millisecond) - StartTimeMs) / 1000,
            Cost = round(DurationSec * Rate * 100) / 100,
            {ok, Cost, State};
        [] ->
            {error, frequency_not_allocated};
        [{_, _OtherName, _}] ->
            {error, occupied_by_other}
    end.

耗时计算实现逻辑

频率使用时长的统计逻辑不需要额外复杂组件,按时间差计算即可:

  1. 客户端申请频率通过校验后,立刻获取当前服务器毫秒级时间戳,和频率ID、客户端标识一起写入DETS持久化存储
  2. 客户端发起释放请求时,再次获取当前服务器时间,减去存储的起始时间戳得到实际使用时长(毫秒),转换为秒单位后乘以5Rs/Sec的费率得到总费用
  3. 因为占用记录做了持久化,即使服务进程中途崩溃重启,起始时间数据不会丢失,重启后释放频率依然可以统计完整的使用周期时长,不会出现计费断层

内容的提问来源于stack exchange,提问作者Shree priya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 07:45:29