正确的Elixir OTP方法来构造重复任务 [英] Proper Elixir OTP way to structure a recurring task

查看:128
本文介绍了正确的Elixir OTP方法来构造重复任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个工作流程,它涉及每30秒左右唤醒一次,并轮询数据库以获取更新,对此采取措施,然后再进入睡眠状态。抛开数据库轮询无法扩展和其他类似的问题,使用主管,工作人员,任务等来构造此工作流的最佳方法是什么?

I've got a workflow that involves waking up every 30 seconds or so and polling a database for updates, taking action on that, then going back to sleep. Setting aside that database polling doesn't scale and other similar concerns, what is the best way to structure this workflow using Supervisors, workers, Tasks, and so forth?

I会列出一些我曾经有过的想法以及我的想法。请帮助我找出最灵丹妙药的方法。 (顺便说一句,我对Elixir还是很陌生。)

I'll lay out a few ideas I've had and my thoughts for/against. Please help me figure out the most Elixir-y approach. (I'm still very new to Elixir, btw.)

1。通过函数调用进行无限循环

只需在其中放置一个简单的递归循环,就像这样:

Just put a simple recursive loop in there, like so:

def do_work() do
  # Check database
  # Do something with result
  # Sleep for a while
  do_work()
end

我在跟随构建网络爬虫的教程

我在这里担心的是由于递归导致的无限堆栈深度。因为我们在每个循环结束时递归,这是否最终会导致堆栈溢出? 标准Elixir任务指南中使用了此结构,所以我可能对堆栈溢出问题有误。

One concern I have here is infinite stack depth due to recursion. Won't this eventually cause a stack overflow since we're recursing at the end of each loop? This structure is used in the standard Elixir guide for Tasks, so I'm probably wrong about the stack overflow problem.

更新-如答案中所述,尾调用递归意味着堆栈溢出在这里不是问题。在末尾调用自己的循环是进行无限循环的一种公认方法。

Update - As mentioned in the answers, tail call recursion in Elixir means stack overflows are not a problem here. Loops that call themselves at the end are an accepted way to do infinite looping.

2。使用任务,每次重新启动

此处的基本思想是使用运行一次然后退出的Task,但将其与带有一对一重新启动策略,因此每次完成后都会重新启动。任务检查数据库,进入睡眠状态,然后退出。主管看到出口并开始一个新的出口。

The basic idea here is to use a Task that runs once and then exits, but pair it with a Supervisor with a one-to-one restart strategy, so it gets restarted each time after it completes. The Task checks the database, sleeps, then exits. The Supervisor sees the exit and starts a new one.

这样做可以使住在主管内的人受益匪浅,但这似乎是对主管的一种滥用。除了错误捕获和重新启动外,它还用于循环。

This has the benefit of living inside a Supervisor, but it seems like an abuse of the Supervisor. It's being used for looping in addition to error trapping and restarting.

(注意:与常规的Supervisor相比,Task.Supervisor可能还可以完成其他操作我只是不明白。)

(Note: There's probably something else that can be done with Task.Supervisor, as opposed to the regular Supervisor and I'm just not understanding it.)

3。任务+无限递归循环

基本上,将1和2结合起来即可使用无限递归循环的Task。现在,它由Supervisor管理,如果崩溃则将重新启动,但不会作为工作流的正常部分一遍又一遍地重新启动。目前,这是我最喜欢的方法。

Basically, combine 1 and 2 so it's a Task that uses an infinite recursion loop. Now it's managed by a Supervisor and will restart if crashed, but doesn't restart over and over as a normal part of the workflow. This is currently my favorite approach.

4。其他吗?

我担心的是,我缺少一些基本的OTP结构。例如,我对Agent和GenServer很熟悉,但是最近我偶然发现了Task。也许正是这种情况下的Looper,或者涵盖了它的Task.Supervisor用例。

My concern is that there's some fundamental OTP structures that I'm missing. For instance, I am familiar with Agent and GenServer, but I just recently stumbled onto Task. Maybe there's some kind of Looper for exactly this case, or some use case of Task.Supervisor that covers it.

推荐答案

我ve只是最近才开始使用OTP,但我想我可以为您提供一些建议:

I've only recently started using OTP, but I think I may be able to give you a few pointers:


  1. 这就是Elixir的方法,我引用了Dave Thomas的Programming Elixir的报价,因为它比我做的要好:

  1. That's the Elixir way of doing this, I took a quote from Programming Elixir by Dave Thomas as it explains better than I do:

递归问候函数可能会让您有些担心。每次
收到一条消息,它最终都会自称。在许多
语言中,这为堆栈添加了新的框架。大量
消息后,您可能会用完内存。在Elixir中,
不会发生这种情况,因为它实现了尾部调用优化。如果
函数做的最后一件事是调用自身,则无需进行调用。
相反,运行时可以简单地跳回到
函数的开始。如果递归调用具有参数,则在循环发生时,这些参数将替换
原始参数。

The recursive greet function might have worried you a little. Every time it receives a message, it ends up calling itself. In many languages, that adds a new frame to the stack. After a large number of messages, you might run out of memory. This doesn’t happen in Elixir, as it implements tail-call optimization. If the very last thing a function does is call itself, there’s no need to make the call. Instead, the runtime can simply jump back to the start of the function. If the recursive call has arguments, then these replace the original parameters as the loop occurs.


  • 任务(如任务模块)适用于单个任务,短暂的流程,因此它们可能就是您想要的。或者,为什么不生成一个进程(可能在启动时)来执行该任务,并使该任务每x次循环并访问数据库呢?

  • 和4,也许考虑使用具有以下体系结构的GenServer主管-> GenServer->工作人员在需要执行任务时生成(这里您可以只使用spawn fn-> ...结束,不需要担心选择Task或其他模块),然后退出完成后。

  • 这篇关于正确的Elixir OTP方法来构造重复任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆