我想知道如何处理与键关联的瞬态gen_servers状态的保存。
为了将键与进程相关联,我使用一个名为 pidstore 的进程。Pidstore最终会启动流程。我给 pidstore 一个键和一个 M,F,A,它在全局中查找键,然后要么返回 pid(如果找到(要么应用 MFA(必须返回 {ok, Pid}(,将 Pid 注册到全局中的键并返回 Pid。
我可能有许多不活跃的gen_servers,状态可能很大。因此,我设置了handle_info回调以将状态保存在数据库中,然后停止该过程。gen_servers在其主管中被视为暂时的,因此在再次需要它们之前不会重新启动它们。
这里开始了问题:如果我用它的键调用一个进程,比如 {car, 23},在表示 {car, 23} 的进程中的 handle_info 保存步骤中,我将按预期返回 pid,因为该过程正在保存并且尚未完成。所以我会用 gen_server:call 调用我的进程,但我永远不会有响应(并点击默认的 5 秒超时(,因为进程正在停止。(问题 A(
为了解决这个问题,进程可以从全局注销自身,然后保存其状态,然后停止。但是如果我在取消注册后但在保存完成之前需要它,我将加载一个新进程,此过程可能会在数据库中加载未更新的值。(问题 B(
为了再次解决此问题,我可以确保数据库中的加载和保存是排队的,并且不能并发。这可能是一个瓶颈。(问题C(
我考虑了另一种解决方案:我的流程在保存之前可以告诉 pidstore 他们很忙。pidstore将保留繁忙进程的列表,并对密钥的任何需求做出"忙碌"响应。保存完成后,PIDstore将被进程告知no_more_busy,并且可以在询问键时启动一个新进程。(即使旧过程没有完成,它也完成了保存,所以他可以慢慢地独自死去(。
这对我来说似乎有点混乱,但进行几次尝试从密钥中获取 Pid 感觉更简单,而不是将每个调用包装到gen_server来处理可能的超时。(当该过程完成但仍在全局注册时(。
我对所有这些半问题和半解决方案都有点困惑。在这种情况下您使用什么设计,或者如何避免这种情况?
我希望我的信息清晰易读,也请告诉我英语错误。
谢谢
也许您想在gen_server:call
中保存到数据库部分。这将防止在写入数据库时传入其他调用。
通常听起来像您已经创建了一个进程寄存器。如果您想在本地注册,您可能需要查看 gproc (https://github.com/uwiger/gproc(,它在这方面做得很好。使用 gproc,您可以完全按照上述操作,使用密钥注册进程。如果您在 init
函数中注册 gproc 并在写入 DB 时取消注册,也许就足够了。您也可以在terminate
函数中写入数据库。
现在我决定坚持 erlang "让它崩溃"哲学。如果进程在关闭时收到消息,则不会应答这些消息,并将触发 gen_server:call/* 超时。
我认为在正确的位置处理此超时会很无聊,我目前尚未决定在哪里,但这是特定于我的应用程序,因此在这里毫无意义。