简体   繁体   English

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

[英]Needed: A Windows Service That Executes Jobs from a Job Queue in a DB; Wanted: Example Code

Needed: 需要:

  • A Windows Service That Executes Jobs from a Job Queue in a DB Windows服务,该服务从数据库中的作业队列执行作业

Wanted: 通缉:

  • Example Code, Guidance, or Best Practices for this type of Application 此类应用程序的示例代码,指南或最佳实践

Background: 背景:

  • A user will click on an ashx link that will insert a row into the DB. 用户将单击ashx链接,该链接将在数据库中插入一行。
  • I need my windows service to periodically poll for rows in this table, and it should execute a unit of work for each row. 我需要Windows服务来定期轮询该表中的行,并且它应该为每行执行一个工作单元。

Emphasis: 重点:

  • This isn't completely new terrain for me. 对我来说,这不是全新的领域。
    • EDIT: You can assume that I know how to create a Windows Service and basic data access. 编辑:您可以假设我知道如何创建Windows服务和基本数据访问。
  • But I need to write this service from scratch. 但是我需要从头开始编写此服务。
  • And I'd just like to know upfront what I need to consider. 我只是想预先知道我需要考虑的内容。
  • EDIT: I'm most worried about jobs that fail, contention for jobs, and keeping the service running. 编辑:我最担心失败的工作,争用工作以及保持服务运行。

Given that you are dealing with a database queue, you have a fair cut of the job already done for you due to the transactional nature of databases. 鉴于您正在处理数据库队列,由于数据库的事务性质,您已经为您完成了相当多的工作。 Typical queue driven application has a loop that does: 典型的队列驱动应用程序具有一个循环,该循环执行:

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

If processing crashes midway, the transaction rolls back and the item is processed on the next service start up. 如果处理中途崩溃,则事务将回滚,并且在下一次服务启动时将处理该项目。

But writing queues in a database is actually a lot trickier than you believe. 但实际上,在数据库中写入队列是很多棘手比你相信。 If you deploy a naive approach, you'll find out that your enqueue and dequeue are blocking each other and the ashx page becomes unresponsive. 如果您采用幼稚的方法,您会发现入队和出队是相互阻塞的,并且ashx页面变得无响应。 Next you'll discover the dequeue vs. dequeue are deadlocking and your loop is constantly hitting error 1205. I strongly urge you to read this article Using Tables as Queues . 接下来,您将发现出队列与出队列处于死锁状态,并且循环不断遇到错误1205。我强烈建议您阅读本文“ 将表用作队列”

Your next challenge is going to be getting the pooling rate 'just right'. 您的下一个挑战将是使池化速率“恰到好处”。 Too aggressive and your database will be burning hot from the pooling requests. 过于激进,您的数据库将因池化请求而变得炙手可热。 Too lax and your queue will grow at rush hours and will drain too slowly. 太松懈,您的队列将在高峰时间增长,并且流失得太慢。 You should consider using an entirely different approach: use a SQL Server built-in QUEUE object and rely on the magic of the WAITFOR(RECEIVE) semantics. 您应该考虑使用完全不同的方法:使用SQL Server内置的QUEUE对象,并依靠WAITFOR(RECEIVE)语义的魔力。 This allows for completely poll free self load tuning service behavior. 这允许完全无轮询的自负载调整服务行为。 Actually, there is more: you don't need a service to start with. 实际上,还有更多:您不需要任何服务。 See Asynchronous Procedures Execution for an explanation on what I'm talking about: launching processing asynchronously in SQL Server from a web service call, in a completely reliable manner. 有关我正在谈论的内容的说明,请参见异步过程执行 :以完全可靠的方式从Web服务调用在SQL Server中异步启动处理。 And finally, if the logic must be in C# process then you can leverage the External Activator , which allows the processing to be hosted in standalone processes as opposed to T-SQL procedures. 最后,如果逻辑必须在C#进程中,则可以利用External Activator ,它允许将处理托管在独立的进程中,而不是T-SQL过程。

First you'll need to consider 首先,您需要考虑

  1. How often to poll for 多久轮询一次
  2. Does your service just stop and start or does it support pause and continue. 您的服务只是停止并启动还是支持暂停并继续。
  3. Concurrency. 并发。 Services can increase the likelihood of a encountering a problem 服务可能会增加遇到问题的可能性

Implementation 实作

  1. Use a System.Timers.Timer not a Threading.Timer 使用System.Timers.Timer而不是Threading.Timer
  2. Maker sure you set the Timer.AutoReset to false. 制作者确保您将Timer.AutoReset设置为false。 This will stop the reentrant problem. 这将解决可重入问题。
  3. Make sure to include execution time 确保包括执行时间

Here's the basic framework of all those ideas. 这是所有这些想法的基本框架。 It includes a way to debug this which is a pain 它包括一种调试方法,这很痛苦

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



 }

EDIT Fixed loop in Start() 编辑 Start()中的固定循环

EDIT Turns out Milliseconds is not the same as TotalMilliseconds 编辑结果毫秒与TotalMilliseconds不同

You may want to have a look at Quartz.Net to manage scheduling the jobs. 您可能需要查看Quartz.Net来管理计划作业。 Not sure if it will fit your particular situation, but it's worth a look. 不知道它是否适合您的特定情况,但是值得一看。

Some things I can think of, based on your edit: 根据您的修改,我可以想到一些事情:

Re: job failure: 回复:工作失败:

  • Determine whether a job can be retried and do one of the following: 确定是否可以重试作业,然后执行以下操作之一:
    • Move the row to an "error" table for logging / reporting later OR 将行移至“错误”表以供以后记录/报告或
    • Leave the row in the queue so that it will be reprocessed by the job service 将行留在队列中,以便作业服务对其进行重新处理
    • You could add a column like WaitUntil or something similar to delay retrying the job after a failure 您可以添加诸如WaitUntil之类的列,或类似于在失败后延迟重试作业的内容

Re: contention: 回复:争用:

  • Add a timestamp column such as "JobStarted" or "Locked" to track when the job was started. 添加时间戳列(例如“ JobStarted”或“ Locked”)以跟踪作业开始的时间。 This will prevent other threads (assuming your service is multithreaded) from trying to execute the job simultaneously. 这将防止其他线程(假设您的服务是多线程的)尝试同时执行作业。
  • You'll need to have some cleanup process that goes through and clears stale jobs for re-processing (in the event the job service fails and your lock is never released). 您将需要执行一些清理过程,并清除陈旧的作业以进行重新处理(如果作业服务失败并且您的锁从未被释放)。

Re: keeping the service running 回复:保持服务运行

  • You can tell windows to restart a service if it fails. 如果服务失败,您可以告诉Windows重新启动服务。
  • You can detect previous failure upon startup by keeping some kind of file open while the service is running and deleting it upon successful shutdown. 通过在服务运行时保持某种文件打开并在成功关闭后将其删除,可以检测到启动时的先前故障。 If your service starts up and that file already exists, you know the service previously failed and can alert an operator or perform the necessary cleanup operations. 如果您的服务启动并且该文件已经存在,则您知道该服务先前已失败,并且可以警告操作员或执行必要的清除操作。

I'm really just poking around in the dark here. 我真的只是在这里黑暗中闲逛。 I'd strongly suggest prototyping the service and returning with any specific questions about the way it functions. 我强烈建议对服务进行原型设计,并返回有关其运行方式的任何特定问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM