如何在 gen_server 模块中使用 gun:open
how to use gun:open in a gen_server module
我有一个 gen_server 模块,我使用 gun 作为 http 客户端与 http 服务器建立长拉连接,所以我在模块的初始化中调用 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}}.
基本上您有两个选择:要么您的进程需要 HTTP 服务器可用(您当前的解决方案),要么不需要,并在与 HTTP 服务器的连接正常关闭时处理请求(通过返回错误反应)。这个博客 post 更雄辩地提出了这个想法: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 进程在这种情况下崩溃,以便它重新启动进入 "not connected" 状态。
我有一个 gen_server 模块,我使用 gun 作为 http 客户端与 http 服务器建立长拉连接,所以我在模块的初始化中调用 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}}.
基本上您有两个选择:要么您的进程需要 HTTP 服务器可用(您当前的解决方案),要么不需要,并在与 HTTP 服务器的连接正常关闭时处理请求(通过返回错误反应)。这个博客 post 更雄辩地提出了这个想法: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 进程在这种情况下崩溃,以便它重新启动进入 "not connected" 状态。