需要:
- 从数据库中的作业队列执行作业的 Windows 服务
通缉:
- 此类应用程序的示例代码、指南或最佳实践
背景:
- 用户将单击 ashx 链接,该链接将在数据库中插入一行。
- 我需要我的 Windows 服务定期轮询此表中的行,并且它应该为每一行执行一个工作单元。
强调:
- 这对我来说并不是全新的领域。
- 编辑:您可以假设我知道如何创建 Windows 服务和基本数据访问。
- 但我需要从头开始编写此服务。
- 我只想提前知道我需要考虑什么。
- 编辑:我最担心工作失败、工作竞争以及保持服务运行。
需要:
通缉:
背景:
强调:
鉴于您正在处理数据库队列,由于数据库的事务性质,您已经为您完成了相当一部分工作。典型的队列驱动应用程序有一个循环:
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)语义的魔力。这允许完全免费轮询自负载调整服务行为。实际上,还有更多:您不需要服务即可开始。有关我正在谈论的内容的解释,请参阅异步过程执行:以完全可靠的方式从 Web 服务调用在 SQL Server 中异步启动处理。最后,,它允许处理托管在独立进程中,而不是 T-SQL 过程。
首先你需要考虑
执行
这是所有这些想法的基本框架。它包括一种调试方法,这很痛苦
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 不同
你可能想看看Quartz.Net来管理调度作业。不确定它是否适合您的特定情况,但值得一看。
根据您的编辑,我能想到的一些事情:
回复:工作失败:
回复:争论:
回复:保持服务运行
我真的只是在黑暗中闲逛。我强烈建议对服务进行原型设计,并返回有关其运行方式的任何具体问题。