如何在 GenServer 中获得有关异步失败的通知?
How can I get notification on async failed in GenServer?
在 elixir GenServer
中,有同步和异步方法,handle_cast
和 handle_call
。在异步情况下,如果该方法失败,我如何获得通知?
方法失败意味着在 handle_call
方法中,我需要定义一些逻辑以 query/write 到数据库。如果数据库操作失败,我需要将此失败通知调用者。在异步方法中,我该怎么做?
所以鼓励您 'let it die' 的评论通常是正确的。惯用的 Erlang 和 Elixir 调用 'failing fast',并让主管重启任何崩溃的组件。
也就是说,有时崩溃是不合适的;通常是在您知道可能会出现负面结果时。标准库中的许多 API 通过返回结果元组来处理此问题,即 {:ok, result}
或 {:error, reason}
并使调用代码负责崩溃或尝试其他操作。
在您的用例中,我认为您应该只从进程中使用数据调用数据库 write/query 代码,根本不使用异步方法,首先修复数据库性能。如果这确实是一个长 运行 查询,并且优化数据库不是正确的答案,那么您的下一个最佳选择是 Task
模块(documentation here),它是 Elixir 标准库的一部分- 它为异步任务执行提供内置功能。
我知道人们不回答你的问题是多么令人沮丧,所以我会回答的;但请注意,这几乎肯定不是解决您最初问题的正确方法。
关键的见解是将调用进程的 pid 传递给 Worker,以便它可以稍后发送结果消息:
defmodule CastBackExampleWorker do
use GenServer
# ...
def do_operation(args) do
caller = self()
ref = make_ref()
# Pass the caller's pid to the GenServer so that it can message back later
GenServer.cast(__MODULE__, {:do_operation, caller, ref, args})
# hand back a unique ref for this request
ref
end
# ...
def handle_cast({:do_operation, caller, ref, args}, state) do
case execute_operation(args) do
{:ok, result} -> send(caller, {__MODULE__, ref, {:ok, result}})
{:error, reason} -> send(caller, {__MODULE__, ref, {:error, reason}})
end
{:noreply, state}
end
end
defmodule CastBackExampleClient do
use GenServer
# ...
def handle_call(:my_real_work, _from, state) do
# ignoring the ref, but we could stick it into our state for later...
_ref = CastBackExampleWorker.do_operation([])
{:reply, :ok, state}
end
def handle_info({CastBackExampleWorker, _ref, outcome}, state) do
# Do something with the outcome here
{:noreply, state}
end
end
在 elixir GenServer
中,有同步和异步方法,handle_cast
和 handle_call
。在异步情况下,如果该方法失败,我如何获得通知?
方法失败意味着在 handle_call
方法中,我需要定义一些逻辑以 query/write 到数据库。如果数据库操作失败,我需要将此失败通知调用者。在异步方法中,我该怎么做?
所以鼓励您 'let it die' 的评论通常是正确的。惯用的 Erlang 和 Elixir 调用 'failing fast',并让主管重启任何崩溃的组件。
也就是说,有时崩溃是不合适的;通常是在您知道可能会出现负面结果时。标准库中的许多 API 通过返回结果元组来处理此问题,即 {:ok, result}
或 {:error, reason}
并使调用代码负责崩溃或尝试其他操作。
在您的用例中,我认为您应该只从进程中使用数据调用数据库 write/query 代码,根本不使用异步方法,首先修复数据库性能。如果这确实是一个长 运行 查询,并且优化数据库不是正确的答案,那么您的下一个最佳选择是 Task
模块(documentation here),它是 Elixir 标准库的一部分- 它为异步任务执行提供内置功能。
我知道人们不回答你的问题是多么令人沮丧,所以我会回答的;但请注意,这几乎肯定不是解决您最初问题的正确方法。
关键的见解是将调用进程的 pid 传递给 Worker,以便它可以稍后发送结果消息:
defmodule CastBackExampleWorker do
use GenServer
# ...
def do_operation(args) do
caller = self()
ref = make_ref()
# Pass the caller's pid to the GenServer so that it can message back later
GenServer.cast(__MODULE__, {:do_operation, caller, ref, args})
# hand back a unique ref for this request
ref
end
# ...
def handle_cast({:do_operation, caller, ref, args}, state) do
case execute_operation(args) do
{:ok, result} -> send(caller, {__MODULE__, ref, {:ok, result}})
{:error, reason} -> send(caller, {__MODULE__, ref, {:error, reason}})
end
{:noreply, state}
end
end
defmodule CastBackExampleClient do
use GenServer
# ...
def handle_call(:my_real_work, _from, state) do
# ignoring the ref, but we could stick it into our state for later...
_ref = CastBackExampleWorker.do_operation([])
{:reply, :ok, state}
end
def handle_info({CastBackExampleWorker, _ref, outcome}, state) do
# Do something with the outcome here
{:noreply, state}
end
end