2012-06-20 38 views
2

新的Erlang和只是有点麻烦让我的头围绕新的范式!erlang主管最好的方式来处理ibrowse:send_req conn_failed

OK,所以我有一个OTP gen_server在这个内部函数:

my_func() -> 
Result = ibrowse:send_req(?ROOTPAGE,[{"User-Agent",?USERAGENT}],get), 
case Result of 
    {ok, "200", _, Xml} -> %<<do some stuff that won't interest you>> 
,ok; 
{error,{conn_failed,{error,nxdomain}}} -> <<what the heck do I do here?>> 
end. 

如果我离开了的情况下处理,然后失败,我得到传播到主管的退出信号的连接,它被关闭与服务器一起。

我想要发生的事情(至少我认为这是我想要发生的事情)是,在连接失败时,我想暂停,然后重试send_req说10次,那时管理员可能会失败。

如果我做了什么丑像这样...

{error,{conn_failed,{error,nxdomain}}} -> stop() 

它关闭服务器进程,是的,我明白,直到它不能用我的(尝试在10秒内10次)重启战略,也是期望的结果,但是当我真的想返回{error,error_but_please_dont_fall_over_mr_supervisor}时,从服务器到管理员的返回值是'ok'。

我强烈怀疑在这种情况下,我应该处理所有的业务内容,比如在'my_func'中重试失败的连接,而不是试图让流程停止,然后让管理员重新启动它以便尝试再次。

问题:在这种情况下,“Erlang方式”是什么?

回答

2

我是新来的erlang ..但是这样的事情呢?

代码很长,只是因为评论。我的解决方案(我希望我已经正确理解你的问题)将获得最大尝试次数,然后执行尾递归调用,该调用将通过与下一次尝试的最大尝试次数进行模式匹配来停止。使用timer:sleep()来暂停以简化事情。

%% @doc Instead of having my_func/0, you have 
%% my_func/1, so we can "inject" the max number of 
%% attempts. This one will call your tail-recursive 
%% one 
my_func(MaxAttempts) -> 
    my_func(MaxAttempts, 0). 

%% @doc This one will match when the maximum number 
%% of attempts have been reached, terminates the 
%% tail recursion. 
my_func(MaxAttempts, MaxAttempts) -> 
    {error, too_many_retries}; 

%% @doc Here's where we do the work, by having 
%% an accumulator that is incremented with each 
%% failed attempt. 
my_func(MaxAttempts, Counter) -> 
    io:format("Attempt #~B~n", [Counter]), 
    % Simulating the error here. 
    Result = {error,{conn_failed,{error,nxdomain}}}, 
    case Result of 
     {ok, "200", _, Xml} -> ok; 
     {error,{conn_failed,{error,nxdomain}}} -> 
      % Wait, then tail-recursive call. 
      timer:sleep(1000), 
      my_func(MaxAttempts, Counter + 1) 
    end. 

编辑:如果这个代码是在被监督的过程中,我认为这是最好有一个simple_one_for_one,在那里你可以dinamically任何你需要的工人,增加,这是为了避免延迟初始化由于超时(以一个one_for_one工作人员按顺序启动,并在那时进行睡眠将停止其他进程的初始化)。

EDIT2:添加了一个示例壳执行:

1> c(my_func). 
my_func.erl:26: Warning: variable 'Xml' is unused 
{ok,my_func} 
2> my_func:my_func(5). 
Attempt #0 
Attempt #1 
Attempt #2 
Attempt #3 
Attempt #4 
{error,too_many_retries} 

对于每个印刷信息之间1S延迟。

+0

是的,我尝试了一些类似的东西(比你的解决方案略差一些),传递尝试的次数然后递减并测试为零。唯一的区别是我调用stop()而不是返回一个元组,停止会关闭服务器(并重新启动它),但不会返回有用的消息,{error,too_many_retries}将返回一条有用的消息,但不会关闭服务器。 – unclejimbob

+0

我希望找到一个解决方案来获得这两个世界的组合,因为send_req错误出现我重试了10次,然后如果它仍然失败,我抓住它,然后再次抛出,但以避免导致监督员失败 - 但我也认为这不是'Erlang的方式'。我认为你的解决方案是要走的路,因为如果发生任何其他异常,它应该由主管的重启战略覆盖。 如果其他人有不同的方法或意见,那么一定要通过。 非常感谢 – unclejimbob

+0

@unclejimbob从我所了解的情况来看,只有在达到最大尝试次数时,才想让监督人员失败,对吗?然后让监督员重新启动工作人员,以便从顶部重试整个操作。这是我试图用代码做的事情。你可能想用erlang来补充它:error(too_many_attempts)。所以,只有在达到最大尝试或抛出未知错误(未捕获)时,才会“让它崩溃”。我不喜欢定时器:sleep()是你的工作人员不服从sys消息,所以另一种解决方案是接收超时 – marcelog

相关问题