如何使用gun:在gen_server模块中打开

时间:2018-09-20 10:32:35

标签: erlang otp gun

我有一个gen_server模块,我使用gun作为http客户端与http服务器建立长拉连接,因此我在模块的init中调用gun:open,但是如果gun:open失败,则我的模块失败,所以我应用程序无法启动。什么是执行此操作的正确方法。以下是我的代码:

init() ->
    lager:debug("http_api_client: connecting to admin server...~n"),
    {ok, ConnPid} = gun:open("localhost", 5001),
    {ok, Protocol} = gun:await_up(ConnPid),
    {ok, #state{conn_pid = ConnPid, streams = #{},protocol =  Protocol}}.

1 个答案:

答案 0 :(得分:2)

基本上,您有两个选择:您的进程需要HTTP服务器可用(您当前的解决方案),或者没有,并且在与HTTP服务器的连接正常关闭时处理请求(通过返回错误响应) 。这篇博客文章更雄辩地提出了这个想法:https://ferd.ca/it-s-about-the-guarantees.html

您可以通过将此代码分离到一个单独的函数中来实现,如果连接失败,该函数不会崩溃:

try_connect(State) ->
    lager:debug("http_api_client: connecting to admin server...~n"),
    case gun:open("localhost", 5001) of
        {ok, ConnPid} ->
            {ok, Protocol} = gun:await_up(ConnPid),
            State#state{conn_pid = ConnPid, streams = #{},protocol =  Protocol};
        {error, _} ->
            State#state{conn_pid = undefined}
    end.

然后从init调用此函数。也就是说,无论您是否可以连接,gen_server都将启动。

init(_) ->
    {ok, try_connect(#state{})}.

然后,当您向该gen_server发出要求存在连接的请求时,请检查其是否为undefined

handle_call(foo, _, State = #state{conn_pid = undefined}) ->
    {reply, {error, not_connected}, State};
handle_call(foo, _, State = #state{conn_pid = ConnPid}) ->
    %% make a request through ConnPid here
    {reply, ok, State};

当然,这意味着如果启动时连接失败,您的gen_server将永远不会尝试再次连接。您可以添加计时器,也可以添加显式reconnect命令:

handle_call(reconnect, _, State = #state{conn_pid = undefined}) ->
    NewState = try_connect(State),
    Result = case NewState of
                 #state{conn_pid = undefined} ->
                     reconnect_failed;
                 _ ->
                     ok
             end,
    {reply, Result, NewState};
handle_call(reconnect, _, State) ->
    {reply, already_connected, State}.

当gen_server运行时连接断开时,上面的代码无法处理这种情况。您可以显式地处理该问题,或者在这种情况下可以让gen_server进程崩溃,以使其重新启动到“未连接”状态。