Erlang:我应该以编写任务调度程序结束啥监督树?
Posted
技术标签:
【中文标题】Erlang:我应该以编写任务调度程序结束啥监督树?【英文标题】:Erlang: what supervision tree should I end with writing a task scheduler?Erlang:我应该以编写任务调度程序结束什么监督树? 【发布时间】:2011-07-21 10:23:05 【问题描述】:主要是出于教育目的,我正在尝试编写一个任务(任务是一个 open_port(spawn_executable, Command))调度程序。
我最终得到了像这样的树
supervisor
| |
scheduler receiver
gen_event gen_event
|
supervisor
|
dispatcher
gen_server
|
supervisor
| | |
task1 ... taskN
换句话说:
-
***主管启动调度程序和接收器并确保它们处于活动状态
receiver 启动中级主管
中间主管启动调度程序并确保它处于活动状态
调度员启动底层主管
底层主管根据请求启动任务,并确保在出现错误时重新启动它们
在任何时候调度程序都准备好接受带有时间戳的任务,它应该在以下时间执行
当满足时间戳时,它会通知一些 event_manager 然后接收器由同一个事件管理器通知,并通过中间主管将消息传递给调度器 dispatcher 有一些业务逻辑,这就是为什么它不是无状态的,例如,某些类型的任务不能同时执行 当满足所有条件时,调度程序将任务传递给底层主管,确保任务一直执行,直到正常退出或绕过某些阈值 底层主管返回一条消息,然后向上向上传递给某个事件管理器 调度程序最终收到此消息,从其队列中删除任务或重新入队或其他方式问题是:
-
我使用的行为对吗?
结构是不是太复杂了? (但是,将来该系统将变得分布式。)
有没有办法将receiver+middle supervisor和dispatcher+bottom supervisor组合成两个模块,而不是四个同时实现4个行为?
或者有没有办法将receiver+dispatcher+bottom supervisor组合在一个模块中,省去中间supervisor,同时实现gen_event+gen_server+supervisor行为?
我是否错误地将行为视为 OO 语言中的接口或多重继承? (这让我提出问题 3 和 4。)
提前致谢。
P。 S. IMO,一方面结构过于复杂;另一方面,这样的结构让我可以将它的任何块分发(例如,多个调度器到一个接收器,一个调度器到多个接收器,多个调度器到多个接收器,每个接收器有多个调度器,甚至每个调度器有多个底层监督器) - 每一层都有自己的监督策略)。复杂性和可扩展性之间的平衡点在哪里?
【问题讨论】:
对我来说调度器的作用并不清楚,你需要一个额外的调度器做什么? 考虑替换 cron 来更精确地运行任务:每分钟一次以上;在运行中/运行后具有业务逻辑(即在成功运行某个任务后生成另一个任务);使用自定义错误处理(错误时重新启动/取消/忽略/等);分布式。 如果你想要 OTP 主管树,就必须有主管树,所以永远不要把主管放在主管之外。 【参考方案1】:我的建议是简化您的设计,更像是:
supervisor
| |
dispatcher |
+scheduler |
|
supervisor
| | |
task1 ... taskN
单独的调度程序将事件发送到启动任务等的调度程序并没有太大的好处。即使在分布的情况下也是如此。
dispatcher-scheduler 可以很简单地在 timer 模块的帮助下完成,并且可以是一个 gen_server。 Timer 可以发送消息,您可以在 handle_info 回调中处理这些消息,也可以调用 gen_server 的 api 函数。
您还可以使用超时功能在下一个间隔后唤醒 gen_server,这会更简单,因为您不必担心在添加新“任务”时取消计时器。
调度器/调度器然后调用supervisor:start_child
添加工作任务。
可以轻松添加分发:调度程序/调度程序可以在与二级主管不同的节点上。任务启动功能可以进一步分发,可能使用pool模块进行负载均衡。
回答你的五个问题:
我怀疑您在不需要的地方使用了 gen_event,但由于不需要模块本身,因此可以通过删除它们轻松修复。 gen_event 是如果您希望能够在一个事件源上注册多个处理程序,您可以 1:1 使用它。监督树通常由监督者构建而成,监督者是其他监督者的直接子级。
是的,它太复杂了,看起来有点像用 OO 语言做的,表达能力较差。并且只是为可能的分发做准备是没有必要的。用像 Erlang 这样的函数式语言进行重构比你想象的要容易得多。因此,如果您看到需要,请开始使用简单和拆分功能。
3+4。看看我完全不同的建议。
-
它不像 OO 那样。 OTP 中的行为只是将流程机制隐藏在通用模块中的回调模块。
即使是我建议的简单结构,也有很大的灵活性(由 Erlang 提供给您),因为如果您想拥有多个调度程序,您可以使用 rpc 来调用主管。您可以使用pool 自动负载均衡分配任务。并且调度程序部分可以很容易地与调度程序分离(然后都在***主管下),您可以将更常见的状态与调度程序分离。
【讨论】:
非常感谢您的回答。我自己也考虑过这样的结构,但还有一部分我不太明白。 Dispatcher 和 scheduler 都会有一些特定的业务逻辑,这就是为什么我认为它们应该在不同的模块中实现(也是为了分发)。我再次使用 OO 方法,它们肯定是 OO 中的两个不同的类 - 抱歉,也许我在 Erlang 的情况下弄错了。而我不明白的事情 - 在这种情况下他们应该如何沟通?基因事件?皮德!信息?那么调度员应该有什么行为呢? 还有另一个问题——一个模块可以实现多个行为吗?一个模块应该实现不止一种行为吗?一个模块如何实现多种行为? 为了阐明我想要实现的将调度程序与调度程序分开的目标:我想要一些我们在 OO 中称为控制反转的东西,即。 e.当一个类依赖另一个类时,我们引入第三个类并使两个类都依赖于第三个类,但不再相互依赖。 gen_event 事件管理器似乎是成为“第三类”的合适人选——在将调度程序和调度程序实现为 gen_event 事件处理程序之后,它们不再需要相互了解。但在这种情况下,如果我是对的,调度程序应该同时实现 gen_event 和 gen_server。 如果你想从 Erlang 中受益,我认为你必须离开 OO 思维。它的方法与 OO 完全不同,因此不适用相同的技术。如果您试图在 OO 方法和 Erlang 之间找到 1:1 的匹配,那么您将无法从使用 Erlang 中获得任何好处。 如果你想分离调度器和调度器,你可以这样做。制作两个 gen_servers。通过调用回调模块的 API 函数而不是发送消息进行通信。函数 API 总是具有更好的封装性,并且更易于理解和记录。内部发生的情况:API 函数调用gen_server:call
或 gen_server:cast
,这将导致在幕后发送消息,接下来将调用您的 handle_call 或 handle_cast 处理程序。以上是关于Erlang:我应该以编写任务调度程序结束啥监督树?的主要内容,如果未能解决你的问题,请参考以下文章