最佳的定时任务运行方式

238

今天我们为我们的ASP.NET网站构建了一个控制台应用程序来运行定期任务。但我认为这种方法有些容易出错且难于维护。你是如何在Windows/IIS/ASP.NET环境中执行计划任务的?

更新:

任务示例:

  • 从数据库中的电子邮件队列发送电子邮件
  • 从数据库中移除过时的对象
  • 从Google AdWords检索统计信息并填充数据库中的表格。

你正在运行哪些任务的例子? - JeffO
4
你需要检查 http://atrigger.com/。 - user172163
如果您使用Plesk,可以编写一个vbs并将其添加到任务计划程序:http://www.net24.co.nz/kb/article/AA-00213/13/Shared-Hosting/ASP-ASP.NET/Using-the-Plesk-Windows-Scheduler-to-call-an-ASP-or-.NET-page.html - HasanG
16个回答

130

Jeff Atwood提出的这种技术是我遇到的最简单的方法。它依赖于内置在ASP.NET缓存系统中的“缓存项已移除”回调机制。

更新:Stackoverflow已经超越了这种方法。它只在网站运行时有效,但对许多人来说这是一个非常简单实用的技术。

还要查看Quartz.NET


12
如果应用程序没有运行,但是预定的任务需要执行,会发生什么情况? - MichaelGG
2
如果任务需要运行一段时间,这也可能会减慢用户体验。我更喜欢不让用户为我生成维护/任务。 - Brettski
6
这真的太可怕了!任何问题都可能导致应用程序停止运行,而且只有在下一个用户请求时才会重新启动。在这些时间之间,您的任务将不会运行! - teedyay
44
今天我在原博客文章的评论中注意到了一件事:Jeff Atwood说:“不,我们已经转向专门的任务。我们肯定已经超越了这种技术。但我认为对于小型网站来说还是可以的!” - Joel Coehoorn
3
这种方法存在一个很大的缺陷。每当应用程序被回收时,“CacheItemRemoved”事件会触发,导致您的计划任务会运行。在生产环境中,这可能每天会发生几次。如果您希望任务每周运行,那么情况就不太理想了。 - Steven de Salas
显示剩余7条评论

76
我在网站中保留了所有需要安排的任务,并从特定页面调用它们。然后,我编写了一个简单的Windows服务,每隔一段时间调用此页面。页面运行后会返回一个值。如果我知道还有更多的工作要做,我会立即再次运行该页面;否则,我会稍后再运行它。这对我非常有效,可以将所有任务逻辑与Web代码保持一致。在编写简单的Windows服务之前,我使用了Windows计划程序来每隔x分钟调用该页面。
另一种方便的方法是使用像Pingdom这样的监控服务。将其http检查指向运行您的服务代码的页面。使页面返回结果,然后可以使用这些结果触发Pingdom发送警报消息,以便在出现问题时及时通知您。

3
这是我最终采用的解决方案。与使用自定义网络服务不同,我使用了Windows计划任务和curl。使用Windows服务的好处是什么? - Niels Bosma
16
你看过缓存项过期技术吗?我认为你会发现它是一个更理想的定时服务实现方式:http://www.codeproject.com/KB/aspnet/ASPNETService.aspx - Richard Clayton
11
有什么方法可以阻止恶意用户(或搜索引擎蜘蛛)访问此页面,并触发您预定的任务运行? - UpTheCreek
9
通过在网络应用程序中存储静态时间戳并仅在未设置时间戳(第一次调用)或自上次调用以来正确的时间已过期时才运行,可以停止恶意电话。 - Rob Kent
6
我通过检查IP地址是否为内部地址来阻止对我的URL的恶意调用。如果它不属于我们的组织内部,则不返回任何内容,也不执行任何操作。 - user1408767
显示剩余12条评论

32

创建一个自定义的Windows服务

我之前将一些业务关键任务设置为定时控制台应用程序,但发现难以维护。因此我创建了一个带有“心跳”的Windows服务,每隔几分钟便会检查数据库中的计划任务。这种方式运转效果非常好。

尽管如此,对于大多数非关键性维护任务,我仍使用定时控制台应用程序。如果它没有出问题,就不要去修复它。


7
知悉,链接已失效。 - Charles Burns
2
@CharlesBurns,您可以随时使用archive.org浏览失效的链接:https://web.archive.org/web/20090919131944/http://www.dotheweb.net/articles/dotnet/services.aspx - Nikolay Kostov
关于控制台应用程序和Windows服务,难以维护的是什么?我们使用控制台脚本来执行所有任务,但调度程序是数据库引擎作业代理。我们正在考虑在其周围构建一些Web GUI,并在应用程序级别上管理访问权限(调度程序将成为其自己的Web应用程序的一部分)。 - Muflix

17

我认为这对所有人都很容易:

  • 创建一个名为 DoSuchAndSuchProcess 的 Web 服务方法。
  • 创建一个调用此 Web 方法的控制台应用程序。
  • 在任务计划程序中安排控制台应用程序。

使用这种方法,所有业务逻辑都包含在您的 Web 应用程序中,但您可以依靠 Windows 任务管理器或任何其他商业任务管理器来启动它并记录任何返回信息,例如执行报告。与发布到页面相比,使用 Web 服务具有一定的优势,因为从 Web 服务获取返回数据更容易。


10
使用Threading和Timer类,避免重复造轮子。
    protected void Application_Start()
    {
        Thread thread = new Thread(new ThreadStart(ThreadFunc));
        thread.IsBackground = true;
        thread.Name = "ThreadFunc";
        thread.Start();
    }

    protected void ThreadFunc()
    {
        System.Timers.Timer t = new System.Timers.Timer();
        t.Elapsed += new System.Timers.ElapsedEventHandler(TimerWorker);
        t.Interval = 10000;
        t.Enabled = true;
        t.AutoReset = true;
        t.Start();
    }

    protected void TimerWorker(object sender, System.Timers.ElapsedEventArgs e)
    {
        //work args
    }

7
启动定时器不需要生成线程... - Zyo
4
我也不认为这是一个理想的解决方案。 - Liron Harel
12
@IdanShechter,能否提供一个原因,解释为什么您认为这不是一个理想的解决方案? - Omu
2
线程可能会有问题,因为它不是 HttpRequest,一些涉及到 HttpRequest 的数据可能为空。例如:HttpRequest.ApplicationPath。如果任务编写正确,则可以正常工作。另一个问题是应用程序池的重启。如果该池经常重启(如存在内存泄漏等问题),则工作程序将永远无法运行。 - Tomas Kubes
4
如果您有多个实例正在运行(例如负载均衡)- 可能不希望采用此解决方案(多个任务执行相同操作)。 - Illidan

9
使用Windows Scheduler运行网页。
为了防止恶意用户或搜索引擎蜘蛛运行它,当您设置计划任务时,只需使用查询字符串调用网页,例如:mypage.aspx?from=scheduledtask
然后在页面加载时,只需使用条件: if (Request.Querystring["from"] == "scheduledtask") { //执行任务 }
这样,没有搜索引擎蜘蛛或恶意用户能够执行您的计划任务。

6
最好使用一个加盐哈希算法,比起魔法短语能够更安全地验证查询字符串。请求应该是类似于mypage.aspx?salt=foo&hash=1223523jbhdgu13t1这样的形式。你只需要在服务器和客户端上使用相同的哈希算法即可。同时,在服务器上添加一个硬限制,记录执行时间并防止过快执行。我可能有点偏执。 - Joel Peltonen
16
为了更加安全,可以使用Windows调度程序运行一个检查请求是否来自服务器本身的网页:Request.IsLocal >> 只有服务器本身才能运行计划任务,其他人无法运行。 - The Conspiracy
你有更多使用类似方法的经验吗?我打算开发一个类似于我们应用的服务,现在我无法决定是更好地调用带有计划任务或cron的页面,还是使用缓存系统。 - JayDee

4

3

此外,如果您的应用程序使用SQL SERVER,可以使用SQL Agent来安排任务。这是我们通常放置基于数据驱动的重复代码(例如电子邮件提醒、定期维护、清除等)的地方。 SQL Agent内置了一个很棒的功能,即故障通知选项,可以在关键任务失败时向您发出警报。


2
我不确定您指的是什么类型的定时任务。如果您指的是“每小时刷新 foo.xml”类型的任务,则可以使用Windows计划任务系统(通过“at”命令或控制器)。让它运行控制台应用程序或请求一个特殊页面来启动进程。
编辑:我应该补充说,这也是在预定时间点运行IIS应用程序的一种可行方法。因此,假设您想每30分钟检查数据库并向用户发送有关某些数据的提醒电子邮件,则可以使用预定任务请求此页面,从而使IIS处理事务。
如果您的需求更为复杂,则可以考虑创建Windows服务,并使其运行循环以执行所需的处理。这也具有将代码分离出来以进行扩展或管理目的的好处。缺点是您需要处理Windows服务。

2
如果您拥有服务器,应该使用Windows任务计划程序。从命令行使用AT /?以查看选项。
否则,在基于Web的环境中,您可能需要做一些不好的事情,比如设置一个不同的机器来定时请求某个页面。

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