2017-04-20 109 views
1

我目前正在通过Elixir in Action,我正在对我的Todo应用程序代码进行一些重构,以便更好地掌握OTP的主要部分。在主管中有逻辑可以吗?

该应用程序使用一个数据库,它只是将数据存储在磁盘上的文件中。为确保数据库的目标文件夹存在,在数据库进程中调用File.mkdir_p!(db_folder)。数据库进程本身使用一堆工作进程来执行从磁盘实际存储/检索数据。

我目前的章节介绍了DIY流程注册表,通过让工作人员向注册管理机构注册并让数据库进程查找使用注册表的工作人员来实施更加强大的监督树,从而双方都可以受到监督并会在失败后继续工作。

当Elixir 1.4出来时,我在补丁说明中看到了Registry模块,所以我想我可能会重构应用程序并使用它。现在事实证明,数据库进程并不需要知道数据库用于存储数据的文件夹。所以我把mkdir_p!叫出来,并考虑把它放在哪里。两个选项浮现在脑海中:

  1. DatabaseWorker
  2. DatabaseWorkerSupervisor

我个人比较倾向于第二种方法,因为整个应用程序绑定,如果用户没有访问权限反正崩溃持久性文件夹。但我不确定是否可以将逻辑放入Supervisor中。

根据情况是否将逻辑变为主管不良风格或可接受?如果风格不好,我会在哪里放置启动逻辑,如果某个进程崩溃,我不想重复该启动逻辑?


我的导师代码:

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 

回答

2

我在哪里把启动逻辑,我不想重复,如果一个进程崩溃?

从主管init调用此似乎是合乎逻辑的地方。 特别是如果你不希望它被重复。

也许代码可以在另一个模块中定义,但是从supervisor init调用它是有意义的,并且如果初始化失败,让主管崩溃。

+0

我选择了这个答案,因为它直接回答了原来的问题。但是请注意,[这个答案](http://stackoverflow.com/a/43519122/4050456)如果我想'Database'是'GenServer'也是很有意义的。 –

2

我宁愿不把逻辑的主管。如果你开始把逻辑放在主管人员身上,你不能通过查看监督树来推理崩溃/重新启动。相反,我会建议下列监督树:

suggested supervision tree

如果你把文件夹创建在DB根服务器的init,该DBSupervisor将等待init就移动到他的其他孩子之前回来。所以如果文件夹创建失败,监督树的其余部分甚至不会产生。另外,如果DBSupervisor策略为:rest_for_all,则DB代服务器中的任何故障都将重启监督树的其余部分。

我知道这个答案可能看起来像是矫枉过正,但如果强调要纠正和学习,我认为这是正确的方向。

一个重要说明。正如你所说的,通过注册,你并不需要DB Gen服务器将任务从客户端传递给工作人员,而且你是对的!虽然建议的监督树看起来类似于注册之前的监督树,但您现在应该调用函数(可以在DB模块中实现)来查询注册表并将任务直接从客户机进程传递给工作人员。

+0

Sasa将数据库模块中的GenServer行为从数据库模块中移除了几页,因为它不再保持状态,只是执行注册表查找和委派。在那种情况下,你会在哪里放置文件夹? –

+1

我认为我的建议仍然存在,即使DB gen服务器负责的唯一的事情是在初始化时创建文件夹。再次,这是一个矫枉过正的问题,但想法是学习适当的OTP开发。但也许我错了。让我们看看还有什么建议... – Nagasaki45

相关问题