我目前正在浏览 Elixir in Action ,我正在对我的Todo
应用程序代码进行一些重构,以便更好地掌握OTP的主要部分。
应用程序使用的数据库只是将数据存储在磁盘上的文件中。要确保存在数据库的目标文件夹,请在数据库进程内调用File.mkdir_p!(db_folder)
。数据库进程本身使用一堆工作进程来执行从磁盘实际存储/检索数据。
我目前的章节介绍了一个DIY流程注册表来实现一个更强大的监督树,让工作人员将自己注册到注册表并让数据库进程使用注册表查找工作人员,这样双方都可以受到监督,失败后仍然可以使用。
当Elixir 1.4问世时,我读到了补丁说明中的Registry
模块,所以我想我可能会重构应用程序并使用它。现在结果是,数据库进程实际上不必了解数据库用于存储数据的文件夹。所以我从那个模块中取出了mkdir_p!
电话并考虑了把它放在哪里。我想到了两个选择:
DatabaseWorker
DatabaseWorkerSupervisor
我个人更喜欢第二种方法,因为如果用户对持久性文件夹没有访问权限,整个应用程序肯定会崩溃。但我不太确定将逻辑放入主管是否可以。
根据具体情况,将逻辑置于主管的不良风格还是可接受的?如果它是不好的样式,我在哪里放置启动逻辑,如果进程崩溃我不想重复?
我的主管代码:
defmodule Todo.DatabaseWorkerSupervisor do
use Supervisor
def start_link(db_folder) do
Supervisor.start_link(__MODULE__, db_folder)
end
def init(db_folder) do
File.mkdir_p!(db_folder)
processes =
for worker_id <- 1..3 do
worker(Todo.DatabaseWorker, [db_folder, worker_id], id: {:dbworker, worker_id})
end
supervise(processes, strategy: :one_for_one)
end
end
答案 0 :(得分:2)
我不想把逻辑放在主管身上。如果您开始将逻辑放在主管中,则无法通过查看监督树来推断崩溃/重启。相反,我会建议以下监督树:
如果您将文件夹创建放在DB gen服务器的init
中,DBSupervisor将等待init
返回,然后再转移到其他孩子。因此,如果文件夹创建失败,则监督树的其余部分甚至不会产生。此外,如果DBSupervisor策略为:rest_for_all
,则DB gen服务器中的任何故障都将重新启动监督树的其余部分。
我知道这个答案可能看起来有点矫枉过正,但如果重点是纠正和学习,我认为这是正确的方向。
一个重要的注意事项。正如你所说,通过注册,你并不需要DB gen服务器来将任务从客户传递给工人,你是对的!虽然建议的监督树看起来与注册之前的类似,但您现在应该只调用函数(可以在数据库模块中实现)来查询注册表并将任务直接从客户端进程传递给工作者。
答案 1 :(得分:2)
如果进程崩溃,我在哪里放置我不想重复的启动逻辑?
从主管init
调用此类似乎是合乎逻辑的地方。
特别是如果你不想重复它。
也许代码可以在另一个模块中定义,但是从supervisor init调用它是有意义的,如果初始化失败,让主管崩溃。