有一些您可能知道但未在问题中描述的要求使得给出明智的答案具有挑战性。其中一些问题是:
- 任务必须成功完成吗?
- 如果任务成功/未成功完成,“谁”需要知道以及需要执行什么类型的操作?
- 如果再次运行任务时任务还没有完成,会有什么行为?它应该运行还是不运行?
- 作业以指定的时间间隔运行有多重要?如果间隔是每 5 分钟一次,是否必须每 5 分钟一次,或者任务是否可以在 5 分 10 秒后运行?
第一步是回答如何安排定期任务运行。一个选项是 Windows 计划任务,但它本质上不是高度可用的,但可能可以解决这个问题。如果您使用的是 SQL Server,另一种选择是使用 SQL Server 代理作为调度程序,因为它将作为 SQL Server 的一部分进行故障转移。
下一步要确定的是如何调用 WCF 应用程序。最简单的选项是触发作业以通过 NLB IP 地址调用 WCF 服务。如果数据库服务器(或该区域中的其他服务器)正在调用应用程序区域(当然,总是有例外,例如 MSDTC),这可能被认为是禁止的。
另一种选择是使用队列模型。在大多数情况下,这将是最可靠的。例如,SQL Server 代理可以执行存储过程以在队列表中输入记录。然后在每个应用程序服务器上,服务可以轮询以查找要处理的排队记录。对队列中记录的访问将由数据库序列化,以便第一个服务器运行该作业(并且该作业只会运行一次)。
根据此答案中开放问题的答案,您可能需要添加更多错误处理。如果外部资源的检索通常很短,您可能希望简单地使用 a 锁定队列记录,select for update
并在任务完成时更新状态(或者如果您愿意,可以删除记录)。这将阻止其他服务实例在另一台服务器上处理记录时处理该记录,如果在处理期间发生崩溃,则应回滚事务,并且集群中的另一个服务可以获取该记录。(不过,您可以将事务超时增加到您认为需要的时间。)
如果长时间保持数据库锁定不可行,那么您可以更改逻辑并为服务添加一些监控。现在,当一个作业开始处理时,它的状态将从排队变为运行,并且正在处理记录的服务器将在记录上更新。可以创建某种服务状态表,每个服务实例每次轮询时都会更新当前时间。这将允许集群中的其他服务重新处理显示为正在运行但它们应该在其上运行的服务在一定时间内没有“签入”的作业。
这种方法也有局限性:如果任务确实完成但不知何故数据库连接丢失了怎么办——该作业可能会再次运行。当然,我不认为将原子数据库操作与其他非事务性资源(例如 Web 请求、文件系统)结合起来的问题会很容易解决。我假设您正在编写文件或其他东西——如果外部内容也被放入数据库中,那么单个事务将保证一切都是一致的。