处理Web服务超时,同时执行长时间运行的数据库任务
我们的产品之一的体系结构是一个典型的3层解决方案:处理Web服务超时,同时执行长时间运行的数据库任务
- C#客户端
- WCF Web服务
- SQL Server数据库
客户端向Web服务请求信息。 Web服务访问数据库以获取信息并将其返回给客户端。
这是问题所在。其中一些查询可能需要很长时间,而我们并不知道哪些查询会很慢。我们知道一些通常比其他通信更慢,但即使是最简单的请求也可以在给出足够的数据的情况下缓慢。有时使用查询或运行大量数据的报告。这些查询只能在大量数据减慢之前进行优化。
如果数据库中的查询达到SQL Server中的最大查询超时时间,数据库查询将终止,并且Web服务会向客户端返回错误。这是可以理解的。我们可以处理这些错误。
客户端正在等待Web服务调用完成。如果数据库调用需要很长时间,则客户端可能会在对Web服务的调用中超时。客户放弃,但数据库请求继续处理。此时,客户端与数据库不同步。数据库调用可能成功也可能不成功。可能有错误。客户永远不会知道。在某些情况下,我们不希望我们的用户启动另一个请求,因为前一个请求的完成可能会导致无效状态。
我很好奇看看别人如何处理这个问题。您使用什么策略防止Web服务超时影响数据库调用?
我能想出的最好的想法包括在Web服务内部的某个地方建立一个实际的数据库层 - 连接到一个消息队列 - 一些东西。将每个查询卸载到另一个进程看起来过多。 (然后,我们并不总是知道给定请求是快速还是慢速)
如果我们能够将发出HTTP请求的行为与发起和运行数据库进程的行为分开。我已经在前一家公司的一个自定义服务器上看到了这一点,但它使用直接套接字通信,并且我宁愿避免用一些自定义应用程序来替换Web服务。
请注意,考虑到我们处理的数据量,我们都在查询优化。查询优化,索引等仅在数据量很大时才会带您到达目的地。有时事情需要很长时间。
我们最近使用的解决方案之一是将巨大的数据库进程分解为单独的并行操作。每个操作都要小得多,并且设计得尽可能高效。客户启动操作,产生几个线程,并且尽其所能地并行执行。
例如,我们已经将一些巨大的过程分解为一系列步骤,如“开始”,“过程1块工作”,“完成”和“收集报告数据”。流程工作步骤可以并行运行,但在启动步骤完成之前无法启动。 Finish步骤需要等待所有Process Work步骤完成。
由于客户端正在控制进程,客户端可以准确报告进程的进度。
Web服务可以在线程池中运行查询,并且如果线程没有在5秒内完成(参见Thread.Join()),则Web服务调用将返回客户端的JobID而不是结果集,其中客户端可以随后使用每隔几秒轮询服务器以查看其查询是否完成。当线程完成后,结果可以存储在散列表中,直到客户端再次轮询。
我遇到过去similiar问题,并用以下3种方法之一来解决它:
- 将所有长时间运行的查询到队列,并处理这些顺序。
在我的情况下,这些都是复杂的报告,然后通过电子邮件发送给客户或存储在永久“临时”表格中供客户在收到通知后查看。 - 我们使用JQuery调用调用webservice,然后在完成时调用JavaScript回发方法。
当我们不想让页面加载与Web服务正在做的事情同步时,这种方式运行良好。
但是,这确实意味着该功能在长时间运行过程完成之前不可用。 - 最复杂的一个。
我们弹出另一个窗口,其中显示了一个进度条,它也定期轮询服务器。
它使用一个会话变量来确定显示进度条的距离。
启动进度条后,开始一个新的线程,它定期更新同一个会话变量。
将会话变量值设置为100后,弹出窗口自动关闭。
客户喜欢这种方法。
无论如何,我希望这些对你有一些帮助。
将问题分解为小块当然是一个好主意。
除此之外,别人说什么(只有当你在web服务的实现手中)我一直在使用传递给web服务的回调URL。 WS必须在查询字符串或发布数据中调用错误或结果。
网址通常包含一个令牌,用于允许回调重新进入客户端,并且映射到接收到回调(存储在数据库或内存中)后执行操作所需的任何相关信息。
这有点沉重(特别是如果你没有在网络服务器上运行),但保证在客户端超时但Web服务正确接收到指令并且处理速度缓慢的情况下成功往返。
一旦设置好了,你的web服务实际上已经更接近于准备好异步运行,从而迅速回应客户端:通常,做任何检查以回答它是否可行,并且在单独的循环,与回调url,以便它可以报告给客户端。
我不确定这是多么正统,顺便说一句,但它确实解决了实际问题。