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