Azure工作角色 - 后台任务计划

4

我有一个Azure存储表格,其中包含一堆任务需要在特定时间由工作角色完成。例如:

       Task 1: -> run every 5 min
       Task 2: -> run every 1 min
       Task 3: -> run every 10 min
       Task 4: -> run every 1 min
       Task 5: -> run every 5 min
       ...........................
       Task 1000: -> run every 1 min

这种方法正确吗: 每个任务都有一个名为“LastRun”的列DateTime。另外还有一列名为“RunEvery”,存储任务应该执行的时间。工作角色不断地迭代所有任务,并且对于每个任务,使用以下方法检查“LastRun”列:
      DateTime currentTime = DateTime.Now;
      if (currentTime >= (myTask.LastRun + myTask.RunEvery))
      {
           myTask.Execute()
      }
      else
      {
           Check.Next.Task.InTable();
      }

如果工作角色持续运行,资源消耗会怎样?我们该如何节约资源?或者我能用更好的方式实现吗?您有什么建议吗?


我可能已经写了你正在寻找的东西,几天前。这是一个依赖于Azure Tables的C#调度程序,专为低事务消耗(每50个计划任务3个表存储事务)而设计,用于多个角色实例的上下文中使用。计划任务是“事务性”的,这意味着当任务完成时,你需要调用.Handled();(否则它将在“x”小时后再次触发)。如果感兴趣,我还需要稍微清理一下代码,然后我可以在github上发布它。 - uzul
非常感兴趣!看起来很有意思!请告诉我何时准备好了!提前致谢! - David Dury
我正在准备发布它。 - uzul
@uzul,你能告诉我文章发布的时间和在哪里找到它吗?非常感谢! - David Dury
6个回答

12

除了@Simon Munro的回答之外,另一种在工作角色中实现任务调度而无需外部调度程序依赖的方法是利用Quartz库 (http://quartznet.sourceforge.net/)。我在其中一个项目中使用过它,效果非常好。它在任务调度方面给您提供了很大的灵活性。你仍然需要利用blob租赁和Windows Azure队列来解决worker role的多个实例之间的并发问题。

更新: 受此启发,我撰写了一篇有关此事的博客文章,您可以在这里阅读: http://gauravmantri.com/2013/01/23/building-a-simple-task-scheduler-in-windows-azure/.


2
另一种实现任务调度而不依赖外部调度程序的方法是利用Quartz库。在我看来,Quartz库似乎是一个外部依赖项。 - uzul
也许我应该澄清一下我所说的外部依赖是什么 - 我指的是外部服务(例如Simon提到的Aditi云服务)。Quartz库被集成到您的项目中,并且是您项目的一部分。 - Gaurav Mantri
我明白了 :) 顺便说一下,这是一篇很棒的文章。 - uzul

3
滚动自己的调度并不是一个好主意。除非你锁定正在读取的数据,否则你会遇到各种问题。例如,您能否将同一工作角色扩展到数十个或数百个,并确保每个作业仅运行所需的次数。您可能需要使用诸如blob存储上的租约之类的东西来“锁定”您的任务。
虽然您要查看的作业数量可能太高,但一个好的方法是使用像setcronjob这样的cron作业服务,以及新发布的aditi云服务。您可以将任务实现为Web服务,并将其连接到外部服务。
就资源利用而言,每隔几分钟触发事件的计时器不会使用太多资源。您可以有一个单独的线程,根据从队列中读取的任务执行任务(即使ConcurrentQueue),因此您一次只执行一个任务(如果精确的时间不是问题)。其他线程/计时器/事件可以将任务添加到队列中。

1
你目前的方法似乎无法扩展到多个工作角色。
我建议进行一些更改:
1. 使用存储队列来存储准备执行的任务。当任务准备好运行时,向队列添加一条消息,这样其他工作角色就可以参与执行任务了。您还可以使用队列来隐藏任务,直到它准备好运行为止。 2. 当您正在读取和更新调度表时,锁定Blob资源,这样只有一个工作角色可以安排任务。
请记住,您的任务可能会被执行两次(在极少数情况下),因此请尽量设计以解决此问题。
为避免频繁访问队列或表,请考虑指数退避并使用Thread.Sleep,如果您的队列和调度为空。 此博客包含更多详细信息,可能有助于您的实现。

1
虽然Gaurav Mantri写了一篇很棒的文章,展示了如何以安全的方式将Quartz与Azure存储队列/表/块相连,但这种解决方案并不符合我正在开发的应用程序的要求。同时使用队列/块/表可能会导致Azure交易成本过高,这是我的主要担忧之一。
我目前正在开发一个需要调度大量任务的Azure应用程序,因此几天前我编写了自己的“家庭”解决方案,但它远远没有Quartz的质量高,目前只是一个原型,尚未经过充分测试,但在我看来似乎工作正常。
设计目标:
- 尽可能优化存储事务。这是通过仅使用RangeQueries和BatchOperations,并尽可能地对事务进行分组来完成的。仅使用3个存储事务即可安排和获取50个任务。 - 每个ScheduledTask都必须得到适当的“提交”(否则稍后会再次启动)。 - 简单且非侵入式的API - Scheduler类是线程安全的,并且应该在多个实例间整体上是安全的
并发问题是通过使用删除操作来解决的,如果任务在同一时间已被出队,则删除操作将失败。(在内部处理)

我刚刚发布了项目这里。这并不是最初的意图,只是为了被视为这样。请在发现错误时告诉我。


0

如果任务不需要太频繁地运行,一种方法是创建一个Azure SQL表,并为每个执行生成一行。作为列,您将拥有执行时间和应该运行的任务的某个标识符。因此,如果任务每天运行一次,并且您希望它持续运行5年,那么您将放置5 * 365行。

工作程序将运行无限循环,从该表中选择执行时间小于当前时间但尚未执行的任务。对于多个工作程序,您需要使用事务来确保每个任务仅由一个工作程序执行。

或者,您甚至可以使用类似的Azure Service Bus机制。服务总线支持计划交付,并且似乎没有消息存活时间的上限。使用服务总线,您只需推送每个计划执行的消息,并将交付时间设置为执行时间。然后,工作程序将从队列中弹出消息。

使用服务总线的好处之一是,您可以轻松添加更多的工作程序,而不必担心它们会开始处理相同的任务。


0

可能是在回答一个老问题。但是,与其使用诸如 Cron 等重量级库(其中有很多),也许值得花点时间学习 Rx 反应式扩展,并在那里使用计时器。Rx wiki 上的一个简单示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接