如何在目标进程不存在时捕获 GenServer.call(destination, ...) 失败

How to catch GenServer.call(destination, ...) failure when destination process doesn't exist

在我的应用程序中,所有登录的用户都由 GenServer 表示,我调用 UserAgent,它基本上将每个用户的状态保存在内存中。所有这些进程都在基于 Horde 的分布式注册表中使用唯一 ID 在集群中注册。每当用户执行某些操作时,客户端应用程序都会将要执行的操作与 user_id 一起发送。在服务器端,控制器检查参数(强制、可选、语法等)并最终调用 UserAgent.the_action(user_id, other_params)the_action(...) 函数只是向服务器发送一条消息,其中包含要执行的操作:GenServer.call(via_tuple(id), {:the_action, params}).

在某些情况下,user_id 引用的 UserAgent 不再存在,例如因为用户已经闲置一段时间并且进程已被清除(会话已过期)或因为当时无法访问集群的某些部分(目前的假想用例)。在这些情况下,调用 GenServer.call(via_tuple(id), {:the_action, params}) 会导致下面的错误,这也会使 HTTP 端点进程(下面的 #PID<0.1359.0>)崩溃,进而导致 500 HTTP 错误(dump api 调用,嗯,为了调试目的转储进程的状态):

[error] #PID<0.1359.0> running AppWeb.Endpoint (connection #PID<0.1358.0>, stream id 1) terminated
Server: localhost:4001 (http)
Request: GET /api/v1/dump/5f534b99d6ca3fe1ff6d2f78
** (exit) exited in: GenServer.call({:via, Horde.Registry, {App.DReg, "5f534b99d6ca3fe1ff6d2f78"}}, :dump, 5000)
    ** (EXIT) no process: the process is not alive or there's no process currently associated \
        with the given name, possibly because its application isn't started

我只是想不出如何拦截这个错误。实际上,我最终在 UserAgent 的客户端部分调用 Horde.Registry.lookup(App.UserAgent.via_tuple(user_id)),然后调用 GenServer.call() with the returned pid` 或者如果找不到进程则向控制器返回错误。

想知道有没有更好的方法

这就是GenServer.call/3handles errors in . It nevertheless calls whereis/1, so you might either call whereis/1 yourself or replicate logic from the code I linked or use Kernel.SpecialForms.try/1catch的异常。