ERLang OTP gen_server:call() 失败

Posted

技术标签:

【中文标题】ERLang OTP gen_server:call() 失败【英文标题】:ERLang OTP gen_server:call() fails 【发布时间】:2014-12-24 12:48:16 【问题描述】:

我编写了一个 gen_server 模块 (data_cahe.erl),它将数据保存在 ETS 中。

我的代码如下:

-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
 terminate/2, code_change/3]).

-define(SERVER, ?MODULE).
-define(TABLE_ID, ?MODULE).
-record(state, user_id, my_reading, my_status).

start_link() ->
gen_server:start_link(local, ?SERVER, ?MODULE, [], []).

init([]) ->
ok, ?TABLE_ID = new_cache(?TABLE_ID),
ok, #stateuser_id=undefined, my_reading=undefined, my_status=undefined.

handle_call:

handle_call(save, UserId, Readings, _From, _Status) ->
io:format("Inside handle_call_save: ~n~p~n",[]);
%Check if email is present
case my_reading(UserId) of
error, not_found -> %%Email not present
    io:format("Inside handle_call_save Just before save: ~n~p~n",[]),
    Result = save_my_readings(UserId, Readings),
    reply, ok, #stateuser_id=UserId, my_reading=Readings, my_status=Result;
ok, Reading ->
    io:format("Inside handle_call_save Just before delete and save: ~n~p~n",[]),
    delete_my_reading(UserId), %%delete last reading
    Result = save_my_readings(UserId, Readings), %%Save this new Reading
    reply, ok, #stateuser_id=UserId, my_reading=Readings, my_status=Result
end;

我正在尝试使用这个handel_call(可以访问电子邮件和AccessToken)将来自工作模块的数据保存在ETS中:

case my_app_interface:get_data_summary(binary_to_list(AccessToken)) of
    error, _Reason1 ->
    %%Start a new Timer Cycle
    ..
    ..
    Readings1 ->
        gen_server:call(data_cahe, save, Email, Readings1), %%HERE IT CRASHES
        io:format("Get Data Summary : ~n~p~n",[Readings1]) %%This is printed when the line above is commented
 end,

但是 gen_server:call(...) 崩溃了。当我注释掉这一行时,读数会按通常的顺序打印。

除了handle_call 方法中的打印语句之外,我什至已经删除了所有行——但没有打印任何内容。似乎 gen_server:call(...) 根本没有通过。如果有人指出出了什么问题,将不胜感激。

【问题讨论】:

由于您的程序崩溃,如果包含错误消息会很好。 【参考方案1】:

也许你拼错了? data_cahe 而不是 ddata_cache..

【讨论】:

【参考方案2】:

作为一般规则,您不希望将服务器的用户暴露给gen_server API。或者更具体地说,您不希望他们调用gen_server:call( ModuleName, SomeAtom, And, DifferentArguments),因为它为许多错误(拼写错误和消息元组中缺少“参数”)创造了空间。而且很难找出如何与该服务器交互(必须查看handle_callhandle_cast,这不是最简单的方法)。

要解决这个问题,所有此类交互(所有可能的调用和强制转换)都应该包含在函数调用中。这些功能将作为模块接口公开(导出)。最后,客户端甚至不必知道它是用gen_server实现的

所以如果你的模块被称为data_cache,并且你有保存一些数据的功能,只需实现save函数。

save( Email, Readings) ->
   gen_server:call(?SERVER, save, Email, Readings1).

我们甚至使用了?SERVER 宏(对可能的拼写错误有所帮助),您可以保留handle_call 原样。现在可以将客户端调用更改为

    Readings1 ->
        data_cache:save(Email, Readings1),
        io:format("Get Data Summary : ~n~p~n",[Readings1]) %%This is printed when the line above is commented
 end,

它更容易阅读,更难破解。

【讨论】:

感谢 mpm 的输入。真的很感激!

以上是关于ERLang OTP gen_server:call() 失败的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 Erlang 中使用 OTP?

Erlang/OTP 升级丢失现有的 RabbitMQ 消息

Erlang/OTP 发布和 LTS 时间表是啥?

erlang驱动使用mysql-otp

您如何设计基于 Erlang/OTP 的分布式容错多核系统的架构?

Erlang/OTP 生产应用部署简介