需要:从数据库中的作业队列执行作业的Windows服务;通缉:示例代码

时间:2010-07-16 15:26:45

标签: c# .net windows-services sample

需要:

  • 从数据库中的作业队列执行作业的Windows服务

通缉:

  • 此类应用程序的示例代码,指南或最佳实践

背景:

  • 用户将点击将在行中插入行的ashx链接。
  • 我需要我的Windows服务定期轮询此表中的行,并且它应该为每一行执行一个工作单元。

重点:

  • 这对我来说并不是一个全新的领域。
    • 编辑:您可以假设我知道如何创建Windows服务和基本数据访问。
  • 但我需要从头开始编写这项服务。
  • 我只想提前了解我需要考虑的事项。
  • 编辑:我最担心失败的工作,争夺工作,以及保持服务正常运行。

4 个答案:

答案 0 :(得分:8)

鉴于您正在处理数据库队列,由于数据库的事务性质,您可以公平地完成已经完成的工作。典型的队列驱动应用程序有一个循环:

while(1) {
 Start transction;
 Dequeue item from queue;
 process item;
 save new state of item;
 commit;
}

如果处理中途崩溃,交易将回滚,并在下次服务启动时处理该项目。

但是在数据库中编写队列实际上比你认为的更强大很多。如果你部署一个天真的方法,你会发现你的入队和出队相互阻塞,ashx页面变得没有响应。接下来你会发现dequeue和dequeue都是死锁,你的循环经常遇到错误1205.我强烈建议你阅读这篇文章Using Tables as Queues

您的下一个挑战是如何将汇集率“恰到好处”。过于激进,您的数据库将从池化请求中炙手可热。太麻烦了,你的队列会在高峰时间增长,而且排水太慢。您应该考虑使用完全不同的方法:使用SQL Server内置的QUEUE对象并依赖WAITFOR(RECEIVE)语义的魔力。这允许完全轮询免费的自我调整服务行为。实际上,还有更多:您不需要服务开始。有关我正在谈论的内容的解释,请参阅Asynchronous Procedures Execution:以完全可靠的方式从Web服务调用中在SQL Server中异步启动处理。最后,如果逻辑必须在C#进程中,那么您可以利用External Activator,这允许处理在独立进程中托管,而不是T-SQL过程。

答案 1 :(得分:4)

首先你需要考虑

  1. 投票的频率
  2. 您的服务是停止启动还是支持暂停并继续。
  3. 并发。服务可能会增加遇到问题的可能性
  4. 实施

    1. 使用System.Timers.Timer而不是Threading.Timer
    2. Maker确定您将Timer.AutoReset设置为false。这将阻止重入问题。
    3. 确保包含执行时间
    4. 这是所有这些想法的基本框架。它包括一种调试方法,这是一种痛苦

              public partial class Service : ServiceBase{
      
              System.Timers.Timer timer;
      
      
              public Service()
              {
      
              timer = new System.Timers.Timer();
              //When autoreset is True there are reentrancy problme 
              timer.AutoReset = false;
      
      
              timer.Elapsed += new System.Timers.ElapsedEventHandler(DoStuff);
          }
      
      
           private void DoStuff(object sender, System.Timers.ElapsedEventArgs e)
           {
      
              Collection stuff = GetData();
              LastChecked = DateTime.Now;
      
              foreach (Object item in stuff)
              {
                  try
                          {
                              item.Dosomthing()
                          }
                          catch (System.Exception ex)
                  {
                      this.EventLog.Source = "SomeService";
                      this.EventLog.WriteEntry(ex.ToString());
                      this.Stop();
              }
      
      
              TimeSpan ts = DateTime.Now.Subtract(LastChecked);
              TimeSpan MaxWaitTime = TimeSpan.FromMinutes(5);
      
      
              if (MaxWaitTime.Subtract(ts).CompareTo(TimeSpan.Zero) > -1)
                  timer.Interval = MaxWaitTime.Subtract(ts).TotalMilliseconds;
              else
                  timer.Interval = 1;
      
              timer.Start();
      
      
      
      
      
           }
      
              protected override void OnPause()
           {
      
               base.OnPause();
               this.timer.Stop();
           }
      
           protected override void OnContinue()
           {
               base.OnContinue();
               this.timer.Interval = 1;
               this.timer.Start();
           }
      
           protected override void OnStop()
           {
      
               base.OnStop();
               this.timer.Stop();
           }
      
      
           protected override void OnStart(string[] args)
           {
              foreach (string arg in args)
              {
                  if (arg == "DEBUG_SERVICE")
                          DebugMode();
      
              }
      
               #if DEBUG
                   DebugMode();
               #endif
      
               timer.Interval = 1;
               timer.Start();
      
              }
      
          private static void DebugMode()
          {
      
              Debugger.Break();
          }
      
      
      
       }
      

      编辑在Start()

      中修复了固定循环

      编辑结果毫秒与TotalMilliseconds不同

答案 2 :(得分:2)

您可能需要查看Quartz.Net来管理作业的计划。不确定它是否适合您的特定情况,但值得一看。

答案 3 :(得分:1)

根据您的修改,我能想到的一些事情:

回复:工作失败:

  • 确定是否可以重试作业并执行以下操作之一:
    • 将行移至“错误”表以便日后记录/报告或
    • 将行留在队列中,以便作业服务
    • 对其进行重新处理
    • 你可以添加像WaitUntil这样的列或类似的东西,以便在失败后延迟重试作业

Re:争论:

  • 添加时间戳列,例如“JobStarted”或“Locked”,以跟踪作业的启动时间。这将阻止其他线程(假设您的服务是多线程的)尝试同时执行作业。
  • 您需要进行一些清理过程并清除过时的作业以进行重新处理(如果作业服务失败且您的锁永远不会被释放)。

Re:保持服务正常运行

  • 如果服务失败,您可以告诉Windows重新启动服务。
  • 您可以通过在服务运行时保持某种文件打开并在成功关闭时删除它来检测启动时的先前故障。如果您的服务已启动并且该文件已存在,则您知道该服务先前已失败,并且可以提醒操作员或执行必要的清理操作。

我真的只是在黑暗中瞎逛。我强烈建议对服务进行原型设计,并回答有关其运行方式的任何具体问题。