.net 需要:一个Windows服务,它执行数据库中作业队列中的作业;需要:示例代码

h7appiyu  于 2023-02-06  发布在  .NET
关注(0)|答案(4)|浏览(111)

需要:

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

招聘:

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

背景:

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

重点:

  • 这对我来说并不陌生。
      • 编辑:**您可以假设我知道如何创建Windows服务和基本数据访问。
  • 但是我需要从头开始编写这个服务。
  • 我只想知道我需要考虑的事情。
      • EDIT:**我最担心的是失败的作业、作业争用以及保持服务运行。
ego6inou

ego6inou1#

假设您正在处理数据库队列,由于数据库的事务性,您已经完成了相当一部分工作。典型的队列驱动应用程序具有一个循环,该循环执行以下操作:

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

如果处理中途崩溃,事务将回滚,并在下一次服务启动时处理该项。
但是在数据库中编写队列实际上比你想象的要复杂很多。如果你使用一种幼稚的方法,你会发现你的入队和出队互相阻塞,ashx页面变得没有响应。接下来你会发现出队和出队死锁,你的循环经常遇到错误1205。我强烈建议你阅读这篇文章Using Tables as Queues
你的下一个挑战是要让池化速率“恰到好处”。过于激进,你的数据库会因为池化请求而变得过热。过于宽松,你的队列会在高峰时间增长,并且排干得太慢。你应该考虑使用一种完全不同的方法:使用SQL Server内置的QUEUE对象,并依赖于WAITFOR(RECEIVE)语义的魔力。这允许完全无轮询的自负载优化服务行为。实际上,还有更多:你不需要一个服务来开始。请参阅Asynchronous Procedures Execution以获得关于我所谈论的内容的解释:在SQL Server中以完全可靠的方式从Web服务调用异步启动处理。最后,如果逻辑必须在C#进程中,则可以利用External Activator,它允许处理托管在独立进程中,而不是T-SQL过程中。

pprl5pva

pprl5pva2#

首先你需要考虑
1.轮询的频率
1.你的服务是停止和启动还是支持暂停和继续。
1.并发性。服务可以增加遇到问题的可能性
执行情况
1.使用System.Timers.Timer而不是Threading.Timer
1.请确保您设置了Timer.AutoReset为false。这将阻止重入问题。
1.确保包括执行时间
下面是所有这些想法的基本框架,其中包括调试这个问题的方法,这是一个痛苦

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();
    }


 }

EDITStart()中的固定循环
EDIT原来毫秒数与总毫秒数不同

wmtdaxz3

wmtdaxz33#

你可能想看看Quartz.Net来管理作业的调度,不确定它是否适合你的特定情况,但值得一看。

6ojccjat

6ojccjat4#

根据你的编辑,我能想到一些事情:
回复:作业失败:

  • 确定是否可以重试作业并执行以下操作之一:
  • 将该行移至“错误”表,以便稍后记录/报告,或者
  • 将该行保留在队列中,以便作业服务重新处理该行
  • 您可以添加一个列,如WaitUntil或类似于延迟失败后重试作业的列

回复:争论:

  • 添加时间戳列(如“JobStarted”或“Locked”)以跟踪作业的启动时间。这将防止其他线程(假设您的服务是多线程的)尝试同时执行作业。
  • 您将需要一些清理进程来遍历和清除陈旧作业以便重新处理(在作业服务失败并且您的锁从未被释放的情况下)。

回复:保持服务运行

  • 您可以告诉windows在服务失败时重新启动它。
  • 通过在服务运行时保持某种类型的文件处于打开状态,并在成功关闭时将其删除,可以在启动时检测到以前的故障。如果服务启动且该文件已存在,则表明服务以前发生了故障,可以向操作员发出警报或执行必要的清理操作。

我真的只是在黑暗中摸索,我强烈建议对服务进行原型化,然后带着关于它的工作方式的任何具体问题返回。

相关问题